diff --git a/.classpath b/.classpath index 9e4872afa32..b68e5e7640b 100644 --- a/.classpath +++ b/.classpath @@ -20,8 +20,8 @@ - - + + diff --git a/app/.classpath b/app/.classpath index 8da1346c0d1..326de565678 100644 --- a/app/.classpath +++ b/app/.classpath @@ -7,8 +7,8 @@ - - + + diff --git a/app/build.xml b/app/build.xml index 70861d75680..5f14148dd85 100644 --- a/app/build.xml +++ b/app/build.xml @@ -99,6 +99,8 @@ + + diff --git a/app/lib/bcpg-jdk15on-149.jar b/app/lib/bcpg-jdk15on-149.jar deleted file mode 100644 index b5306e06d72..00000000000 Binary files a/app/lib/bcpg-jdk15on-149.jar and /dev/null differ diff --git a/app/lib/bcpg-jdk15on-152.jar b/app/lib/bcpg-jdk15on-152.jar new file mode 100644 index 00000000000..4f5c6310573 Binary files /dev/null and b/app/lib/bcpg-jdk15on-152.jar differ diff --git a/app/lib/bcprov-jdk15on-149.jar b/app/lib/bcprov-jdk15on-149.jar deleted file mode 100644 index e1d4bb31ce1..00000000000 Binary files a/app/lib/bcprov-jdk15on-149.jar and /dev/null differ diff --git a/app/lib/bcprov-jdk15on-152.jar b/app/lib/bcprov-jdk15on-152.jar new file mode 100644 index 00000000000..6c54dd901cc Binary files /dev/null and b/app/lib/bcprov-jdk15on-152.jar differ diff --git a/app/src/cc/arduino/contributions/packages/ui/ContributionIndexTableModel.java b/app/src/cc/arduino/contributions/packages/ui/ContributionIndexTableModel.java index cc3c6f56808..25c1506d48c 100644 --- a/app/src/cc/arduino/contributions/packages/ui/ContributionIndexTableModel.java +++ b/app/src/cc/arduino/contributions/packages/ui/ContributionIndexTableModel.java @@ -32,7 +32,7 @@ import cc.arduino.contributions.filters.InstalledPredicate; import cc.arduino.contributions.packages.ContributedPackage; import cc.arduino.contributions.packages.ContributedPlatform; -import cc.arduino.contributions.packages.ContributionsIndex; +import cc.arduino.contributions.packages.ContributionsIndexer; import cc.arduino.contributions.ui.FilteredAbstractTableModel; import com.google.common.base.Predicate; import com.google.common.base.Predicates; @@ -113,16 +113,16 @@ public void select(ContributedPlatform value) { private Class[] columnTypes = {ContributedPlatform.class}; - private ContributionsIndex index; + private ContributionsIndexer indexer; - public void setIndex(ContributionsIndex _index) { - index = _index; + public void setIndexer(ContributionsIndexer indexer) { + this.indexer = indexer; } public void updateIndexFilter(String filters[], Predicate... additionalFilters) { contributions.clear(); Predicate filter = Predicates.and(additionalFilters); - for (ContributedPackage pack : index.getPackages()) { + for (ContributedPackage pack : indexer.getPackages()) { for (ContributedPlatform platform : pack.getPlatforms()) { if (!filter.apply(platform)) { continue; diff --git a/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java b/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java index 85b215ee3d3..af452523521 100644 --- a/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java +++ b/app/src/cc/arduino/contributions/packages/ui/ContributionManagerUI.java @@ -28,15 +28,11 @@ */ package cc.arduino.contributions.packages.ui; -import cc.arduino.contributions.ui.InstallerJDialogUncaughtExceptionHandler; import cc.arduino.contributions.packages.ContributedPlatform; import cc.arduino.contributions.packages.ContributionInstaller; import cc.arduino.contributions.packages.ContributionsIndexer; import cc.arduino.contributions.packages.DownloadableContribution; -import cc.arduino.contributions.ui.DropdownItem; -import cc.arduino.contributions.ui.FilteredAbstractTableModel; -import cc.arduino.contributions.ui.InstallerJDialog; -import cc.arduino.contributions.ui.InstallerTableCell; +import cc.arduino.contributions.ui.*; import cc.arduino.utils.Progress; import processing.app.I18n; @@ -95,7 +91,7 @@ public void setIndexer(ContributionsIndexer indexer) { categoryChooser.removeActionListener(categoryChooserActionListener); - getContribModel().setIndex(indexer.getIndex()); + getContribModel().setIndexer(indexer); categoryFilter = null; categoryChooser.removeAllItems(); @@ -106,7 +102,7 @@ public void setIndexer(ContributionsIndexer indexer) { // Enable categories combo only if there are two or more choices categoryChooser.addItem(new DropdownAllCoresItem()); - Collection categories = indexer.getIndex().getCategories(); + Collection categories = indexer.getCategories(); for (String s : categories) { categoryChooser.addItem(new DropdownCoreOfCategoryItem(s)); } @@ -152,7 +148,8 @@ public void onUpdatePressed() { public void run() { try { setProgressVisible(true, ""); - installer.updateIndex(); + List downloadedPackageIndexFiles = installer.updateIndex(); + installer.deleteUnknownFiles(downloadedPackageIndexFiles); onIndexesUpdated(); } catch (Exception e) { throw new RuntimeException(e); diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index ef424b73bf1..965b37ba607 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -22,6 +22,7 @@ package processing.app; +import cc.arduino.DefaultUncaughtExceptionHandler; import cc.arduino.contributions.DownloadableContributionVersionComparator; import cc.arduino.contributions.VersionHelper; import cc.arduino.contributions.libraries.ContributedLibrary; @@ -321,7 +322,8 @@ protected void onProgress(Progress progress) { lastStatus = progress.getStatus(); } }; - installer.updateIndex(); + List downloadedPackageIndexFiles = installer.updateIndex(); + installer.deleteUnknownFiles(downloadedPackageIndexFiles); indexer.parseIndex(); indexer.syncWithFilesystem(getHardwareFolder()); @@ -342,7 +344,7 @@ protected void onProgress(Progress progress) { System.exit(1); } - ContributedPlatform installed = indexer.getIndex().getInstalled(boardToInstallParts[0], boardToInstallParts[1]); + ContributedPlatform installed = indexer.getInstalled(boardToInstallParts[0], boardToInstallParts[1]); if (!selected.isReadOnly()) { installer.install(selected); @@ -2404,14 +2406,6 @@ static public Image getLibImage(String name, Component who) { } - /** - * Return an InputStream for a file inside the Processing lib folder. - */ - static public InputStream getLibStream(String filename) throws IOException { - return BaseNoGui.getLibStream(filename); - } - - // ................................................................... @@ -2429,17 +2423,22 @@ static public int countLines(String what) { */ static public byte[] loadBytesRaw(File file) throws IOException { int size = (int) file.length(); - FileInputStream input = new FileInputStream(file); - byte buffer[] = new byte[size]; - int offset = 0; - int bytesRead; - while ((bytesRead = input.read(buffer, offset, size - offset)) != -1) { - offset += bytesRead; - if (bytesRead == 0) break; + FileInputStream input = null; + try { + input = new FileInputStream(file); + byte buffer[] = new byte[size]; + int offset = 0; + int bytesRead; + while ((bytesRead = input.read(buffer, offset, size - offset)) != -1) { + offset += bytesRead; + if (bytesRead == 0) break; + } + return buffer; + } finally { + if (input != null) { + input.close(); + } } - input.close(); // weren't properly being closed - input = null; - return buffer; } @@ -2474,20 +2473,25 @@ static public HashMap readSettings(File inputFile) { static public void copyFile(File sourceFile, File targetFile) throws IOException { - InputStream from = - new BufferedInputStream(new FileInputStream(sourceFile)); - OutputStream to = - new BufferedOutputStream(new FileOutputStream(targetFile)); - byte[] buffer = new byte[16 * 1024]; - int bytesRead; - while ((bytesRead = from.read(buffer)) != -1) { - to.write(buffer, 0, bytesRead); - } - to.flush(); - from.close(); // ?? - from = null; - to.close(); // ?? - to = null; + InputStream from = null; + OutputStream to = null; + try { + from = new BufferedInputStream(new FileInputStream(sourceFile)); + to = new BufferedOutputStream(new FileOutputStream(targetFile)); + byte[] buffer = new byte[16 * 1024]; + int bytesRead; + while ((bytesRead = from.read(buffer)) != -1) { + to.write(buffer, 0, bytesRead); + } + to.flush(); + } finally { + if (from != null) { + from.close(); // ?? + } + if (to != null) { + to.close(); // ?? + } + } targetFile.setLastModified(sourceFile.lastModified()); } diff --git a/app/src/processing/app/Preferences.java b/app/src/processing/app/Preferences.java index 23230b36d5b..01d3c145450 100644 --- a/app/src/processing/app/Preferences.java +++ b/app/src/processing/app/Preferences.java @@ -209,7 +209,7 @@ public String toString() { // and linux is all over the map static final int GUI_BIG = 13; - static final int GUI_BETWEEN = 10; + static final int GUI_BETWEEN = 5; static final int GUI_SMALL = 6; // gui elements @@ -237,7 +237,7 @@ public String toString() { JTextField proxyHTTPSPort; JTextField proxyUser; JPasswordField proxyPassword; - + private final JTextField additionalBoardsManagerField; // the calling editor, so updates can be applied @@ -464,6 +464,8 @@ public void actionPerformed(ActionEvent e) { right = Math.max(right, left + d.width); top += d.height + GUI_BETWEEN; + // proxy settings + JPanel proxySettingsContainer = new JPanel(); pane.add(proxySettingsContainer); setupProxySettingsFieldSet(proxySettingsContainer); @@ -472,6 +474,17 @@ public void actionPerformed(ActionEvent e) { right = Math.max(right, left + d.width); top += d.height + GUI_BETWEEN; + // boards manager additional urls + box = Box.createHorizontalBox(); + label = new JLabel(_("Additional Boards Manager URLs: ")); + box.add(label); + additionalBoardsManagerField = new JTextField(30); + box.add(additionalBoardsManagerField); + pane.add(box); + d = box.getPreferredSize(); + box.setBounds(left, top, d.width, d.height); + top += d.height + GUI_BETWEEN; + // More preferences are in the ... label = new JLabel(_("More preferences can be edited directly in the file")); @@ -788,6 +801,8 @@ protected void applyFrame() { Preferences.set("proxy.user", proxyUser.getText()); Preferences.set("proxy.password", new String(proxyPassword.getPassword())); + Preferences.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ",")); + editor.applyPreferences(); } @@ -834,6 +849,8 @@ protected void showFrame(Editor editor) { proxyUser.setText(Preferences.get("proxy.user")); proxyPassword.setText(Preferences.get("proxy.password")); + additionalBoardsManagerField.setText(Preferences.get("boardsmanager.additional.urls")); + dialog.setLocationRelativeTo(editor); dialog.setVisible(true); } diff --git a/app/src/processing/app/Theme.java b/app/src/processing/app/Theme.java index 7f23d3c4602..4100bdfc5fe 100644 --- a/app/src/processing/app/Theme.java +++ b/app/src/processing/app/Theme.java @@ -26,6 +26,7 @@ import java.awt.Color; import java.awt.Font; import java.awt.SystemColor; +import java.io.File; import processing.app.helpers.PreferencesHelper; import processing.app.helpers.PreferencesMap; @@ -45,7 +46,7 @@ public class Theme { static protected void init() { try { - table.load(Base.getLibStream("theme/theme.txt")); + table.load(new File(BaseNoGui.getContentFile("lib"), "theme/theme.txt")); } catch (Exception te) { Base.showError(null, _("Could not read color theme settings.\n" + "You'll need to reinstall Arduino."), te); diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 5e063bc6c82..ac8117cb27f 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -22,16 +22,16 @@ package processing.app; +import processing.app.legacy.PApplet; + +import javax.swing.*; import java.io.BufferedReader; -import java.io.InputStream; +import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.net.URLEncoder; import java.util.Random; -import javax.swing.JOptionPane; - -import processing.app.legacy.PApplet; import static processing.app.I18n._; @@ -126,11 +126,16 @@ public void run() { } - protected int readInt(String filename) throws Exception { + protected int readInt(String filename) throws IOException { URL url = new URL(filename); - InputStream stream = url.openStream(); - InputStreamReader isr = new InputStreamReader(stream); - BufferedReader reader = new BufferedReader(isr); - return Integer.parseInt(reader.readLine()); + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(url.openStream())); + return Integer.parseInt(reader.readLine()); + } finally { + if (reader != null) { + reader.close(); + } + } } } diff --git a/app/src/processing/app/syntax/PdeKeywords.java b/app/src/processing/app/syntax/PdeKeywords.java index 865325da33f..b221796a5f1 100644 --- a/app/src/processing/app/syntax/PdeKeywords.java +++ b/app/src/processing/app/syntax/PdeKeywords.java @@ -61,10 +61,10 @@ static public KeywordMap getKeywords() { try { keywordColoring = new KeywordMap(false); keywordToReference = new Hashtable(); - getKeywords(Base.getLibStream("keywords.txt")); + getKeywords(new File(BaseNoGui.getContentFile("lib"), "keywords.txt")); for (ContributedLibrary lib : Base.getLibraries()) { File keywords = new File(lib.getInstalledFolder(), "keywords.txt"); - if (keywords.exists()) getKeywords(new FileInputStream(keywords)); + if (keywords.exists()) getKeywords(keywords); } } catch (Exception e) { Base.showError("Problem loading keywords", @@ -76,51 +76,56 @@ static public KeywordMap getKeywords() { return keywordColoring; } - static private void getKeywords(InputStream input) throws Exception { - InputStreamReader isr = new InputStreamReader(input); - BufferedReader reader = new BufferedReader(isr); - - String line = null; - while ((line = reader.readLine()) != null) { - //System.out.println("line is " + line); - // in case there's any garbage on the line - //if (line.trim().length() == 0) continue; - - String pieces[] = PApplet.split(line, '\t'); - if (pieces.length >= 2) { - //int tab = line.indexOf('\t'); - // any line with no tab is ignored - // meaning that a comment is any line without a tab - //if (tab == -1) continue; - - String keyword = pieces[0].trim(); - //String keyword = line.substring(0, tab).trim(); - //String second = line.substring(tab + 1); - //tab = second.indexOf('\t'); - //String coloring = second.substring(0, tab).trim(); - //String htmlFilename = second.substring(tab + 1).trim(); - String coloring = pieces[1].trim(); - - if (coloring.length() > 0 && Character.isDigit(coloring.charAt(coloring.length() - 1))) { - // text will be KEYWORD or LITERAL - boolean isKey = (coloring.charAt(0) == 'K'); - // KEYWORD1 -> 0, KEYWORD2 -> 1, etc - int num = coloring.charAt(coloring.length() - 1) - '1'; - byte id = (byte) - ((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num); - //System.out.println("got " + (isKey ? "keyword" : "literal") + - // (num+1) + " for " + keyword); - keywordColoring.add(keyword, id); - } - if (pieces.length >= 3) { - String htmlFilename = pieces[2].trim(); - if (htmlFilename.length() > 0) { - keywordToReference.put(keyword, htmlFilename); + static private void getKeywords(File input) throws IOException { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new FileInputStream(input))); + + String line = null; + while ((line = reader.readLine()) != null) { + //System.out.println("line is " + line); + // in case there's any garbage on the line + //if (line.trim().length() == 0) continue; + + String pieces[] = PApplet.split(line, '\t'); + if (pieces.length >= 2) { + //int tab = line.indexOf('\t'); + // any line with no tab is ignored + // meaning that a comment is any line without a tab + //if (tab == -1) continue; + + String keyword = pieces[0].trim(); + //String keyword = line.substring(0, tab).trim(); + //String second = line.substring(tab + 1); + //tab = second.indexOf('\t'); + //String coloring = second.substring(0, tab).trim(); + //String htmlFilename = second.substring(tab + 1).trim(); + String coloring = pieces[1].trim(); + + if (coloring.length() > 0 && Character.isDigit(coloring.charAt(coloring.length() - 1))) { + // text will be KEYWORD or LITERAL + boolean isKey = (coloring.charAt(0) == 'K'); + // KEYWORD1 -> 0, KEYWORD2 -> 1, etc + int num = coloring.charAt(coloring.length() - 1) - '1'; + byte id = (byte) + ((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num); + //System.out.println("got " + (isKey ? "keyword" : "literal") + + // (num+1) + " for " + keyword); + keywordColoring.add(keyword, id); + } + if (pieces.length >= 3) { + String htmlFilename = pieces[2].trim(); + if (htmlFilename.length() > 0) { + keywordToReference.put(keyword, htmlFilename); + } } } } + } finally { + if (reader != null) { + reader.close(); + } } - reader.close(); } diff --git a/app/test/cc/arduino/packages/contributions/GPGDetachedSignatureVerifierTest.java b/app/test/cc/arduino/packages/contributions/GPGDetachedSignatureVerifierTest.java new file mode 100644 index 00000000000..c4b4c8633ba --- /dev/null +++ b/app/test/cc/arduino/packages/contributions/GPGDetachedSignatureVerifierTest.java @@ -0,0 +1,66 @@ +/* + * 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.packages.contributions; + +import cc.arduino.contributions.GPGDetachedSignatureVerifier; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class GPGDetachedSignatureVerifierTest { + + private GPGDetachedSignatureVerifier GPGDetachedSignatureVerifier; + + @Before + public void setUp() throws Exception { + GPGDetachedSignatureVerifier = new GPGDetachedSignatureVerifier(); + } + + @Test + public void testSignatureSuccessfulVerification() throws Exception { + File signedFile = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json").getFile()); + File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); + File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./public.gpg.key").getFile()); + assertTrue(GPGDetachedSignatureVerifier.verify(signedFile, sign, publickKey)); + } + + @Test + public void testSignatureFailingVerification() throws Exception { + File fakeSignedFile = File.createTempFile("fakeSigned", "txt"); + fakeSignedFile.deleteOnExit(); + File sign = new File(GPGDetachedSignatureVerifierTest.class.getResource("./package_index.json.sig").getFile()); + File publickKey = new File(GPGDetachedSignatureVerifierTest.class.getResource("./public.gpg.key").getFile()); + assertFalse(GPGDetachedSignatureVerifier.verify(fakeSignedFile, sign, publickKey)); + } +} diff --git a/app/test/cc/arduino/packages/contributions/package_index.json b/app/test/cc/arduino/packages/contributions/package_index.json new file mode 100644 index 00000000000..7b0cb6781ee --- /dev/null +++ b/app/test/cc/arduino/packages/contributions/package_index.json @@ -0,0 +1,757 @@ +{ + "packages": [ + { + "name": "arduino", + "maintainer": "Arduino", + "websiteURL": "http://www.arduino.cc/", + "email": "packages@arduino.cc", + "help": { + "online": "http://arduino.cc/en/Reference/HomePage" + }, + "platforms": [ + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.6.2", + "category": "Arduino", + "url": "http://downloads.arduino.cc/cores/avr-1.6.2.tar.bz2", + "archiveFileName": "avr-1.6.2.tar.bz2", + "checksum": "SHA-256:2909a4c6dd6d7497e7e1b5fcaa2f66a100271417510f3a68593b65af8ff78c1c", + "size": "4877442", + "boards": [ + {"name": "Arduino Yún"}, + {"name": "Arduino Uno"}, + {"name": "Arduino Diecimila"}, + {"name": "Arduino Nano"}, + {"name": "Arduino Mega"}, + {"name": "Arduino MegaADK"}, + {"name": "Arduino Leonardo"}, + {"name": "Arduino Micro"}, + {"name": "Arduino Esplora"}, + {"name": "Arduino Mini"}, + {"name": "Arduino Ethernet"}, + {"name": "Arduino Fio"}, + {"name": "Arduino BT"}, + {"name": "Arduino LilyPadUSB"}, + {"name": "Arduino Lilypad"}, + {"name": "Arduino Pro"}, + {"name": "Arduino ATMegaNG"}, + {"name": "Arduino Robot Control"}, + {"name": "Arduino Robot Motor"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino2" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino2" + } + ] + }, + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.6.3", + "category": "Arduino", + "help": { + "online": "http://arduino.cc/en/Reference/HomePage" + }, + "url": "http://downloads.arduino.cc/cores/avr-1.6.3.tar.bz2", + "archiveFileName": "avr-1.6.3.tar.bz2", + "checksum": "SHA-256:c30033ba70cbb2d46ee0901a331b0f83be082f9110eda0464b624fdbb51b3c7b", + "size": "4876816", + "boards": [ + {"name": "Arduino Yún"}, + {"name": "Arduino Uno"}, + {"name": "Arduino Diecimila"}, + {"name": "Arduino Nano"}, + {"name": "Arduino Mega"}, + {"name": "Arduino MegaADK"}, + {"name": "Arduino Leonardo"}, + {"name": "Arduino Micro"}, + {"name": "Arduino Esplora"}, + {"name": "Arduino Mini"}, + {"name": "Arduino Ethernet"}, + {"name": "Arduino Fio"}, + {"name": "Arduino BT"}, + {"name": "Arduino LilyPadUSB"}, + {"name": "Arduino Lilypad"}, + {"name": "Arduino Pro"}, + {"name": "Arduino ATMegaNG"}, + {"name": "Arduino Robot Control"}, + {"name": "Arduino Robot Motor"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino3" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino3" + } + ] + }, + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.6.4", + "category": "Arduino", + "help": { + "online": "http://arduino.cc/en/Reference/HomePage" + }, + "url": "http://downloads.arduino.cc/cores/avr-1.6.4.tar.bz2", + "archiveFileName": "avr-1.6.4.tar.bz2", + "checksum": "SHA-256:8a243410aeded6dbcbc4b134ba10be5c2562d137bfcf3ac97abdc5844933b363", + "size": "4780884", + "boards": [ + {"name": "Arduino Yún"}, + {"name": "Arduino Uno"}, + {"name": "Arduino Diecimila"}, + {"name": "Arduino Nano"}, + {"name": "Arduino Mega"}, + {"name": "Arduino MegaADK"}, + {"name": "Arduino Leonardo"}, + {"name": "Arduino Micro"}, + {"name": "Arduino Esplora"}, + {"name": "Arduino Mini"}, + {"name": "Arduino Ethernet"}, + {"name": "Arduino Fio"}, + {"name": "Arduino BT"}, + {"name": "Arduino LilyPadUSB"}, + {"name": "Arduino Lilypad"}, + {"name": "Arduino Pro"}, + {"name": "Arduino ATMegaNG"}, + {"name": "Arduino Robot Control"}, + {"name": "Arduino Robot Motor"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino5" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino5" + } + ] + }, + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.6.5", + "category": "Arduino", + "help": { + "online": "http://arduino.cc/en/Reference/HomePage" + }, + "url": "http://downloads.arduino.cc/cores/avr-1.6.5.tar.bz2", + "archiveFileName": "avr-1.6.5.tar.bz2", + "checksum": "SHA-256:c72d890aa605add677634c6b25ebc3b2ed9e44c38805b95c47eab17a1ca72db6", + "size": "4876957", + "boards": [ + {"name": "Arduino Yún"}, + {"name": "Arduino Uno"}, + {"name": "Arduino Diecimila"}, + {"name": "Arduino Nano"}, + {"name": "Arduino Mega"}, + {"name": "Arduino MegaADK"}, + {"name": "Arduino Leonardo"}, + {"name": "Arduino Micro"}, + {"name": "Arduino Esplora"}, + {"name": "Arduino Mini"}, + {"name": "Arduino Ethernet"}, + {"name": "Arduino Fio"}, + {"name": "Arduino BT"}, + {"name": "Arduino LilyPadUSB"}, + {"name": "Arduino Lilypad"}, + {"name": "Arduino Pro"}, + {"name": "Arduino ATMegaNG"}, + {"name": "Arduino Robot Control"}, + {"name": "Arduino Robot Motor"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino5" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino5" + } + ] + }, + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.6.6", + "category": "Arduino", + "help": { + "online": "http://arduino.cc/en/Reference/HomePage" + }, + "url": "http://downloads.arduino.cc/cores/avr-1.6.6.tar.bz2", + "archiveFileName": "avr-1.6.6.tar.bz2", + "checksum": "SHA-256:08ad5db4978ebea22344edc5d77dce0923d8a644da7a14dc8072e883c76058d8", + "size": "4876916", + "boards": [ + {"name": "Arduino Yún"}, + {"name": "Arduino Uno"}, + {"name": "Arduino Diecimila"}, + {"name": "Arduino Nano"}, + {"name": "Arduino Mega"}, + {"name": "Arduino MegaADK"}, + {"name": "Arduino Leonardo"}, + {"name": "Arduino Micro"}, + {"name": "Arduino Esplora"}, + {"name": "Arduino Mini"}, + {"name": "Arduino Ethernet"}, + {"name": "Arduino Fio"}, + {"name": "Arduino BT"}, + {"name": "Arduino LilyPadUSB"}, + {"name": "Arduino Lilypad"}, + {"name": "Arduino Pro"}, + {"name": "Arduino ATMegaNG"}, + {"name": "Arduino Robot Control"}, + {"name": "Arduino Robot Motor"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "4.8.1-arduino5" + }, + { + "packager": "arduino", + "name": "avrdude", + "version": "6.0.1-arduino5" + } + ] + }, + { + "name": "Arduino SAM Boards (32-bits ARM Cortex-M3)", + "architecture": "sam", + "version": "1.6.2", + "category": "Arduino", + "url": "http://downloads.arduino.cc/cores/sam-1.6.2.tar.bz2", + "archiveFileName": "sam-1.6.2.tar.bz2", + "checksum": "SHA-256:2d3c8a90bc214947cff1b816d0c2706441398efc78af7984d5250f2e50eddd5f", + "size": "16174730", + "boards": [ + {"name": "Arduino Due"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "arm-none-eabi-gcc", + "version": "4.8.3-2014q1" + }, + { + "packager": "arduino", + "name": "bossac", + "version": "1.3a-arduino" + } + ] + }, + { + "name": "Arduino SAM Boards (32-bits ARM Cortex-M3)", + "architecture": "sam", + "version": "1.6.3", + "category": "Arduino", + "url": "http://downloads.arduino.cc/cores/sam-1.6.3.tar.bz2", + "archiveFileName": "sam-1.6.3.tar.bz2", + "checksum": "SHA-256:0a6e1d5542790e38ba454c796aabbd0e48b07635a5b4d8adc044a4eba959ca27", + "size": "16174017", + "boards": [ + {"name": "Arduino Due"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "arm-none-eabi-gcc", + "version": "4.8.3-2014q1" + }, + { + "packager": "arduino", + "name": "bossac", + "version": "1.3a-arduino" + } + ] + }, + { + "name": "Arduino SAM Boards (32-bits ARM Cortex-M3)", + "architecture": "sam", + "version": "1.6.4", + "category": "Arduino", + "url": "http://downloads.arduino.cc/cores/sam-1.6.4.tar.bz2", + "archiveFileName": "sam-1.6.4.tar.bz2", + "checksum": "SHA-256:e0dc94d8ad0756b79838e99ad7409b08b07e40ed667ebe86eae11644ef7bec0d", + "size": "16174992", + "boards": [ + {"name": "Arduino Due"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "arm-none-eabi-gcc", + "version": "4.8.3-2014q1" + }, + { + "packager": "arduino", + "name": "bossac", + "version": "1.3a-arduino" + } + ] + } + ], + "tools": [ + { + "name": "arm-none-eabi-gcc", + "version": "4.8.3-2014q1", + "systems": [ + { + "host": "i686-mingw32", + "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-windows.tar.gz", + "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-windows.tar.gz", + "checksum": "SHA-256:fd8c111c861144f932728e00abd3f7d1107e186eb9cd6083a54c7236ea78b7c2", + "size": "84537449" + }, + { + "host": "x86_64-apple-darwin", + "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-mac.tar.gz", + "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-mac.tar.gz", + "checksum": "SHA-256:3598acf21600f17a8e4a4e8e193dc422b894dc09384759b270b2ece5facb59c2", + "size": "52518522" + }, + { + "host": "x86_64-pc-linux-gnu", + "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-linux64.tar.gz", + "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-linux64.tar.gz", + "checksum": "SHA-256:d23f6626148396d6ec42a5b4d928955a703e0757829195fa71a939e5b86eecf6", + "size": "51395093" + }, + { + "host": "i686-pc-linux-gnu", + "url": "http://downloads.arduino.cc/gcc-arm-none-eabi-4.8.3-2014q1-linux32.tar.gz", + "archiveFileName": "gcc-arm-none-eabi-4.8.3-2014q1-linux32.tar.gz", + "checksum": "SHA-256:ba1994235f69c526c564f65343f22ddbc9822b2ea8c5ee07dd79d89f6ace2498", + "size": "51029223" + } + ] + }, + { + "name": "bossac", + "version": "1.3a-arduino", + "systems": [ + { + "host": "i686-linux-gnu", + "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-i686-linux-gnu.tar.bz2", + "archiveFileName": "bossac-1.3a-arduino-i686-linux-gnu.tar.bz2", + "checksum": "SHA-256:d6d10362f40729a7877e43474fcf02ad82cf83321cc64ca931f5c82b2d25d24f", + "size": "147359" + }, + { + "host": "x86_64-pc-linux-gnu", + "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-x86_64-pc-linux-gnu.tar.bz2", + "archiveFileName": "bossac-1.3a-arduino-x86_64-pc-linux-gnu.tar.bz2", + "checksum": "SHA-256:c1daed033251296768fa8b63ad283e053da93427c0f3cd476a71a9188e18442c", + "size": "26179" + }, + { + "host": "i686-mingw32", + "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-i686-mingw32.tar.bz2", + "archiveFileName": "bossac-1.3a-arduino-i686-mingw32.tar.bz2", + "checksum": "SHA-256:a37727622e0f86cb4f2856ad0209568a5d804234dba3dc0778829730d61a5ec7", + "size": "265647" + }, + { + "host": "i386-apple-darwin11", + "url": "http://downloads.arduino.cc/tools/bossac-1.3a-arduino-i386-apple-darwin11.tar.bz2", + "archiveFileName": "bossac-1.3a-arduino-i386-apple-darwin11.tar.bz2", + "checksum": "SHA-256:40770b225753e7a52bb165e8f37e6b760364f5c5e96048168d0178945bd96ad6", + "size": "39475" + } + ] + }, + { + "name": "avr-gcc", + "version": "4.8.1-arduino2", + "systems": [ + { + "size": "24443285", + "checksum": "SHA-256:c19a7526235c364d7f62ec1a993d9b495973ba1813869ccf0241c65905896852", + "host": "i386-apple-darwin11", + "archiveFileName": "avr-gcc-4.8.1-arduino2-i386-apple-darwin11.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-i386-apple-darwin11.tar.bz2" + }, + { + "size": "27152002", + "checksum": "SHA-256:24a931877bee5f36dc00a88877219a6d2f6a1fb7abb989fd04556b8432d2e14e", + "host": "x86_64-linux-gnu", + "archiveFileName": "avr-gcc-4.8.1-arduino2-x86_64-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-x86_64-pc-linux-gnu.tar.bz2" + }, + { + "size": "25876628", + "checksum": "SHA-256:2d701b4efbc8cec62dc299cde01730c5eebcf23d7e4393db8cf7744a9bf1d3de", + "host": "i686-linux-gnu", + "archiveFileName": "avr-gcc-4.8.1-arduino2-i686-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-i686-pc-linux-gnu.tar.bz2" + }, + { + "size": "46046691", + "checksum": "SHA-256:2eafb49fb803fa4d2c32d35e24c0b372fcd520ca0a790fa537a847179e382000", + "host": "i686-mingw32", + "archiveFileName": "avr-gcc-4.8.1-arduino2-i686-mingw32.zip", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino2-i686-mingw32.zip" + } + ] + }, + { + "name": "avrdude", + "version": "6.0.1-arduino2", + "systems": [ + { + "size": "264965", + "checksum": "SHA-256:71117cce0096dad6c091e2c34eb0b9a3386d3aec7d863d2da733d9e5eac3a6b1", + "host": "i386-apple-darwin11", + "archiveFileName": "avrdude-6.0.1-arduino2-i386-apple-darwin11.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-i386-apple-darwin11.tar.bz2" + }, + { + "size": "292541", + "checksum": "SHA-256:2489004d1d98177eaf69796760451f89224007c98b39ebb5577a9a34f51425f1", + "host": "x86_64-linux-gnu", + "archiveFileName": "avrdude-6.0.1-arduino2-x86_64-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-x86_64-pc-linux-gnu.tar.bz2" + }, + { + "size": "283209", + "checksum": "SHA-256:6f633dd6270ad0d9ef19507bcbf8697b414a15208e4c0f71deec25ef89cdef3f", + "host": "i686-linux-gnu", + "archiveFileName": "avrdude-6.0.1-arduino2-i686-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-i686-pc-linux-gnu.tar.bz2" + }, + { + "size": "241618", + "checksum": "SHA-256:6c5483800ba753c80893607e30cade8ab77b182808fcc5ea15fa3019c63d76ae", + "host": "i686-mingw32", + "archiveFileName": "avrdude-6.0.1-arduino2-i686-mingw32.zip", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino2-i686-mingw32.zip" + } + ] + }, + { + "name": "avr-gcc", + "version": "4.8.1-arduino3", + "systems": [ + { + "size": "24447175", + "checksum": "SHA-256:28e207c66b3dc405367d0c5e68ce3c278e5ec3abb0e4974e7927fe0f9a532c40", + "host": "i386-apple-darwin11", + "archiveFileName": "avr-gcc-4.8.1-arduino3-i386-apple-darwin11.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-i386-apple-darwin11.tar.bz2" + }, + { + "size": "30556996", + "checksum": "SHA-256:028340abec6eb3085b82404dfc7ed143e1bb05b2da961b539ddcdba4a6f65533", + "host": "x86_64-linux-gnu", + "archiveFileName": "avr-gcc-4.8.1-arduino3-x86_64-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-x86_64-pc-linux-gnu.tar.bz2" + }, + { + "size": "28768022", + "checksum": "SHA-256:37796548ba9653267568f959cd8c7ebfe5b4bce4599898cf9f876d64e616cb87", + "host": "i686-linux-gnu", + "archiveFileName": "avr-gcc-4.8.1-arduino3-i686-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-i686-pc-linux-gnu.tar.bz2" + }, + { + "size": "46046917", + "checksum": "SHA-256:d6f0527793f9800f060408392a99eb290ed205730edbae43a1a25cbf6b6b588f", + "host": "i686-mingw32", + "archiveFileName": "avr-gcc-4.8.1-arduino3-i686-mingw32.zip", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino3-i686-mingw32.zip" + } + ] + }, + { + "name": "avrdude", + "version": "6.0.1-arduino3", + "systems": [ + { + "size": "264682", + "checksum": "SHA-256:df7cd4a76e45ab3767eb964f845f4d5e9d643df950ec32812923da1e9843d072", + "host": "i386-apple-darwin11", + "archiveFileName": "avrdude-6.0.1-arduino3-i386-apple-darwin11.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-i386-apple-darwin11.tar.bz2" + }, + { + "size": "748634", + "checksum": "SHA-256:bb7bff48f20a68e1fe559c3f3f644574df12ab5c98eb6a1491079f3c760434ad", + "host": "x86_64-linux-gnu", + "archiveFileName": "avrdude-6.0.1-arduino3-x86_64-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-x86_64-pc-linux-gnu.tar.bz2" + }, + { + "size": "495482", + "checksum": "SHA-256:96a0cfb83fe0452366159e3bf4e19ff10906a8957d1feafd3d98b49ab4b14405", + "host": "i686-linux-gnu", + "archiveFileName": "avrdude-6.0.1-arduino3-i686-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-i686-pc-linux-gnu.tar.bz2" + }, + { + "size": "241619", + "checksum": "SHA-256:ea59bfc2ee85039c85318b2ba52c47ef0573513444a785b72f59b22586a950f9", + "host": "i686-mingw32", + "archiveFileName": "avrdude-6.0.1-arduino3-i686-mingw32.zip", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino3-i686-mingw32.zip" + } + ] + }, + { + "name": "avr-gcc", + "version": "4.8.1-arduino5", + "systems": [ + { + "size": "24437400", + "checksum": "SHA-256:111b3ef00d737d069eb237a8933406cbb928e4698689e24663cffef07688a901", + "host": "i386-apple-darwin11", + "archiveFileName": "avr-gcc-4.8.1-arduino5-i386-apple-darwin11.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-i386-apple-darwin11.tar.bz2" + }, + { + "size": "27093036", + "checksum": "SHA-256:9054fcc174397a419ba56c4ce1bfcbcad275a6a080cc144905acc9b0351ee9cc", + "host": "x86_64-linux-gnu", + "archiveFileName": "avr-gcc-4.8.1-arduino5-x86_64-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-x86_64-pc-linux-gnu.tar.bz2" + }, + { + "size": "25882375", + "checksum": "SHA-256:7648b7f549b37191da0b0be53bae791b652f82ac3cb4e7877f85075aaf32141f", + "host": "i686-linux-gnu", + "archiveFileName": "avr-gcc-4.8.1-arduino5-i686-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-i686-pc-linux-gnu.tar.bz2" + }, + { + "size": "46044779", + "checksum": "SHA-256:d4303226a7b41d3c445d901b5aa5903458def3fc7b7ff4ffef37cabeb37d424d", + "host": "i686-mingw32", + "archiveFileName": "avr-gcc-4.8.1-arduino5-i686-mingw32.zip", + "url": "http://downloads.arduino.cc/tools/avr-gcc-4.8.1-arduino5-i686-mingw32.zip" + } + ] + }, + { + "name": "avrdude", + "version": "6.0.1-arduino5", + "systems": [ + { + "size": "264894", + "checksum": "SHA-256:41af8d3b0a586853c8317b4fb5163ca0db594a1870ddf680fd988c42166fc3e5", + "host": "i386-apple-darwin11", + "archiveFileName": "avrdude-6.0.1-arduino5-i386-apple-darwin11.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-i386-apple-darwin11.tar.bz2" + }, + { + "size": "292629", + "checksum": "SHA-256:d826cca7383461f7e8adde686372cf900e9cb3afd639555cf2d6c645b283a476", + "host": "x86_64-linux-gnu", + "archiveFileName": "avrdude-6.0.1-arduino5-x86_64-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-x86_64-pc-linux-gnu.tar.bz2" + }, + { + "size": "283121", + "checksum": "SHA-256:5933d66927bce46ababa9b68a8b7f1d53f68c4f3ff7a5ce4b85d7cf4e6c6bfee", + "host": "i686-linux-gnu", + "archiveFileName": "avrdude-6.0.1-arduino5-i686-pc-linux-gnu.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-i686-pc-linux-gnu.tar.bz2" + }, + { + "size": "241634", + "checksum": "SHA-256:41f667f1f6a0ab8df46b4ffacd023176dcdef331d6db3b74bddd37d18cca0a44", + "host": "i686-mingw32", + "archiveFileName": "avrdude-6.0.1-arduino5-i686-mingw32.zip", + "url": "http://downloads.arduino.cc/tools/avrdude-6.0.1-arduino5-i686-mingw32.zip" + } + ] + } + ] + }, + { + "email": "support@intel.com", + "maintainer": "Intel", + "websiteURL": "http://www.intel.com/", + "name": "Intel", + "platforms": [ + { + "name": "Intel i586 Boards", + "version": "1.6.2+1.0", + "category": "Arduino Certified", + "architecture": "i586", + "url": "https://github.com/01org/corelibs-galileo/archive/1.6.2+1.0.tar.gz", + "archiveFileName": "corelibs-galileo-1.6.2.tar.gz", + "checksum": "SHA-256:e20d62b0dccf0d68dbb61d70b866d77134b770b226d6046a61c7e8d55e64e53a", + "size": "272961", + "boards": [ + { + "name": "Galileo" + } + ], + "toolsDependencies": [ + { + "packager": "Intel", + "name": "i586-poky-linux-uclibc", + "version": "1.6.2+1.0" + }, + { + "packager": "Intel", + "name": "sketchUploader", + "version": "1.6.2+1.0" + } + ] + }, + { + "name": "Intel i686 Boards", + "version": "1.6.2+1.0", + "category": "Arduino Certified", + "architecture": "i686", + "url": "https://github.com/01org/corelibs-edison/archive/1.6.2+1.0.tar.gz", + "archiveFileName": "corelibs-edison-1.6.2.tar.gz", + "checksum": "SHA-256:538ab8553f832f56b04df80d44992ecc994b9c296f3fce6902832d97f99811a8", + "size": "271420", + "boards": [ + { + "name": "Edison" + } + ], + "toolsDependencies": [ + { + "packager": "Intel", + "name": "core2-32-poky-linux", + "version": "1.6.2+1.0" + }, + { + "packager": "Intel", + "name": "sketchUploader", + "version": "1.6.2+1.0" + } + ] + } + ], + "tools": [ + { + "name": "i586-poky-linux-uclibc", + "version": "1.6.2+1.0", + "systems": [ + { + "size": "30587705", + "checksum": "SHA-256:5b705d26dc1d8ca8953df6e0c08dcc8584d5be77b584d561f631360fd166677c", + "host": "i386-apple-darwin11", + "archiveFileName": "galileo-toolchain-20150323-osx.tar.bz2", + "url": "http://downloadmirror.intel.com/24806/eng/galileo-toolchain-osx-1.6.2-1.0.tar.bz2" + }, + { + "size": "45948648", + "checksum": "SHA-256:821eb290d7c668c1caa74da30903c13843edc746d41508b35161622ae6279b56", + "host": "i686-mingw32", + "archiveFileName": "galileo-toolchain-20150323-windows.zip", + "url": "http://downloadmirror.intel.com/24806/eng/galileo-toolchain-windows-1.6.2-1.0.zip" + }, + { + "size": "56227185", + "checksum": "SHA-256:935ccad3eaaec34f5de76eceb0f0ecd1372bdab0b7dc8f4241e8260c6f827b72", + "host": "x86_64-linux-gnu", + "archiveFileName": "galileo-toolchain-20150316-linux64.tar.bz2", + "url": "http://downloadmirror.intel.com/24806/eng/galileo-toolchain-linux64-1.6.2-1.0.tar.bz2" + }, + { + "size": "55098166", + "checksum": "SHA-256:1dab7f21e10d0208a6dd2897c36c6f5f55f9372b947225d2b59c3c4ab4777d03", + "host": "i686-linux-gnu", + "archiveFileName": "galileo-toolchain-20150316-linux32.tar.bz2", + "url": "http://downloadmirror.intel.com/24806/eng/galileo-toolchain-linux32-1.6.2-1.0.tar.bz2" + } + ] + }, + { + "name": "core2-32-poky-linux", + "version": "1.6.2+1.0", + "systems": [ + { + "size": "42720934", + "checksum": "SHA-256:fac0b3f00a33ee0531ea0da6d517c170409e25bd5e59f6f3db9506974336375d", + "host": "i386-apple-darwin11", + "archiveFileName": "edison-toolchain-20150323-osx.tar.bz2", + "url": "http://downloadmirror.intel.com/24806/eng/edison-toolchain-osx-1.6.2-1.0.tar.bz2" + }, + { + "size": "56683094", + "checksum": "SHA-256:5a9a1b51f0fa18bf21e1dcf1332d34331dd435c5ca0d1fe008e68e13cb3255e5", + "host": "i686-mingw32", + "archiveFileName": "edison-toolchain-20150323-windows.zip", + "url": "http://downloadmirror.intel.com/24806/eng/edison-toolchain-windows-1.6.2-1.0.zip" + }, + { + "size": "78998436", + "checksum": "SHA-256:e3443e7832732f2189fd424e4868d2ebb563e823addb2321a6e8a86a9fced193", + "host": "x86_64-linux-gnu", + "archiveFileName": "edison-toolchain-20150316-linux64.tar.bz2", + "url": "http://downloadmirror.intel.com/24806/eng/edison-toolchain-linux64-1.6.2-1.0.tar.bz2" + }, + { + "size": "76488215", + "checksum": "SHA-256:014d1bdc40bb080987c736d04ffd42cdc0d2c3cad001891fb01555dac04296f7", + "host": "i686-linux-gnu", + "archiveFileName": "edison-toolchain-20150316-linux32.tar.bz2", + "url": "http://downloadmirror.intel.com/24806/eng/edison-toolchain-linux32-1.6.2-1.0.tar.bz2" + } + ] + }, + { + "name": "sketchUploader", + "version": "1.6.2+1.0", + "systems": [ + { + "size": "61789", + "checksum": "SHA-256:8395ccb57c627f997fe01170df4613de906f48c6ce99623b9ca42806079c28ad", + "host": "i386-apple-darwin11", + "archiveFileName": "intel-arduino-tools-20150316-osx.tar.gz", + "url": "https://github.com/01org/intel-arduino-tools/archive/1.6.2+1.0-osx.tar.gz" + }, + { + "size": "2534586", + "checksum": "SHA-256:47f0b1558653d09f6f1e5d21a9cdef379be96c9147711cf54f013f73e4deed11", + "host": "i686-mingw32", + "archiveFileName": "intel-arduino-tools-20150316-windows.zip", + "url": "https://github.com/01org/intel-arduino-tools/archive/1.6.2+1.0-windows.zip" + }, + { + "size": "178239", + "checksum": "SHA-256:2876db4153db22609d2f6c9c3bfb198efbb9d9574edad579aca7d58cff9d2cca", + "host": "x86_64-linux-gnu", + "archiveFileName": "intel-arduino-tools-20150316-linux64.tar.gz", + "url": "https://github.com/01org/intel-arduino-tools/archive/1.6.2+1.0-linux64.tar.gz" + }, + { + "size": "187995", + "checksum": "SHA-256:20d87602d0194be626f592d3f2bdc9566a5a897786b042393482ef4c26ae158c", + "host": "i686-linux-gnu", + "archiveFileName": "intel-arduino-tools-20150316-linux32.tar.gz", + "url": "https://github.com/01org/intel-arduino-tools/archive/1.6.2+1.0-linux32.tar.gz" + } + ] + } + ] + } + ] +} diff --git a/app/test/cc/arduino/packages/contributions/package_index.json.sig b/app/test/cc/arduino/packages/contributions/package_index.json.sig new file mode 100644 index 00000000000..10c7ff65997 Binary files /dev/null and b/app/test/cc/arduino/packages/contributions/package_index.json.sig differ diff --git a/app/test/cc/arduino/packages/contributions/public.gpg.key b/app/test/cc/arduino/packages/contributions/public.gpg.key new file mode 100644 index 00000000000..5de39fed1f7 Binary files /dev/null and b/app/test/cc/arduino/packages/contributions/public.gpg.key differ diff --git a/arduino-core/.classpath b/arduino-core/.classpath index 136e7f2700e..cd3604c153b 100644 --- a/arduino-core/.classpath +++ b/arduino-core/.classpath @@ -17,10 +17,10 @@ - - - - + + + + diff --git a/arduino-core/lib/bcpg-jdk15on-149.jar b/arduino-core/lib/bcpg-jdk15on-149.jar deleted file mode 100644 index b5306e06d72..00000000000 Binary files a/arduino-core/lib/bcpg-jdk15on-149.jar and /dev/null differ diff --git a/arduino-core/lib/bcpg-jdk15on-152.jar b/arduino-core/lib/bcpg-jdk15on-152.jar new file mode 100644 index 00000000000..4f5c6310573 Binary files /dev/null and b/arduino-core/lib/bcpg-jdk15on-152.jar differ diff --git a/arduino-core/lib/bcprov-jdk15on-149.jar b/arduino-core/lib/bcprov-jdk15on-149.jar deleted file mode 100644 index e1d4bb31ce1..00000000000 Binary files a/arduino-core/lib/bcprov-jdk15on-149.jar and /dev/null differ diff --git a/arduino-core/lib/bcprov-jdk15on-152.jar b/arduino-core/lib/bcprov-jdk15on-152.jar new file mode 100644 index 00000000000..6c54dd901cc Binary files /dev/null and b/arduino-core/lib/bcprov-jdk15on-152.jar differ diff --git a/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java b/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java new file mode 100644 index 00000000000..cd64d59e6d4 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/GPGDetachedSignatureVerifier.java @@ -0,0 +1,112 @@ +/* + * 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.contributions; + +import org.apache.commons.compress.utils.IOUtils; +import org.bouncycastle.openpgp.*; +import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; + +import java.io.*; +import java.util.Iterator; + +public class GPGDetachedSignatureVerifier { + + private String keyId; + + public GPGDetachedSignatureVerifier() { + this("7F294291"); + } + + public GPGDetachedSignatureVerifier(String keyId) { + this.keyId = keyId; + } + + public boolean verify(File signedFile, File signature, File publicKey) throws IOException, PGPException { + PGPPublicKey pgpPublicKey = readPublicKey(publicKey, keyId); + + FileInputStream signatureInputStream = null; + FileInputStream signedFileInputStream = null; + try { + signatureInputStream = new FileInputStream(signature); + PGPObjectFactory pgpObjectFactory = new PGPObjectFactory(signatureInputStream, new BcKeyFingerprintCalculator()); + + PGPSignatureList pgpSignatureList = (PGPSignatureList) pgpObjectFactory.nextObject(); + assert pgpSignatureList.size() == 1; + PGPSignature pgpSignature = pgpSignatureList.get(0); + + pgpSignature.init(new BcPGPContentVerifierBuilderProvider(), pgpPublicKey); + signedFileInputStream = new FileInputStream(signedFile); + pgpSignature.update(IOUtils.toByteArray(signedFileInputStream)); + + return pgpSignature.verify(); + } finally { + if (signatureInputStream != null) { + signatureInputStream.close(); + } + if (signedFileInputStream != null) { + signedFileInputStream.close(); + } + } + } + + private PGPPublicKey readPublicKey(File file, String keyId) throws IOException, PGPException { + InputStream keyIn = null; + try { + keyIn = new BufferedInputStream(new FileInputStream(file)); + return readPublicKey(keyIn, keyId); + } finally { + if (keyIn != null) { + keyIn.close(); + } + } + } + + private PGPPublicKey readPublicKey(InputStream input, String keyId) throws IOException, PGPException { + PGPPublicKeyRingCollection pgpPub = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(input), new BcKeyFingerprintCalculator()); + + Iterator keyRingIter = pgpPub.getKeyRings(); + while (keyRingIter.hasNext()) { + PGPPublicKeyRing keyRing = (PGPPublicKeyRing) keyRingIter.next(); + + Iterator keyIter = keyRing.getPublicKeys(); + while (keyIter.hasNext()) { + PGPPublicKey key = (PGPPublicKey) keyIter.next(); + + if (Long.toHexString(key.getKeyID()).toUpperCase().endsWith(keyId)) { + return key; + } + } + } + + throw new IllegalArgumentException("Can't find encryption key in key ring."); + } + +} diff --git a/arduino-core/src/cc/arduino/contributions/SignatureVerificationFailedException.java b/arduino-core/src/cc/arduino/contributions/SignatureVerificationFailedException.java new file mode 100644 index 00000000000..201e7ec4595 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/SignatureVerificationFailedException.java @@ -0,0 +1,16 @@ +package cc.arduino.contributions; + +import processing.app.I18n; + +import static processing.app.I18n._; + +public class SignatureVerificationFailedException extends Exception { + + public SignatureVerificationFailedException(String filename) { + super(I18n.format(_("{0} file signature verification failed"), filename)); + } + + public SignatureVerificationFailedException(String filename, Throwable cause) { + super(I18n.format(_("{0} file signature verification failed"), filename), cause); + } +} diff --git a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java index 7493d0e085b..c285ade68a4 100644 --- a/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java +++ b/arduino-core/src/cc/arduino/contributions/libraries/LibrariesIndexer.java @@ -70,17 +70,24 @@ public void parseIndex() throws IOException { } private void parseIndex(File indexFile) throws IOException { - InputStream indexIn = new FileInputStream(indexFile); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MrBeanModule()); - mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); - mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - index = mapper.readValue(indexIn, LibrariesIndex.class); - - for (ContributedLibrary library : index.getLibraries()) { - if (library.getCategory() == null || "".equals(library.getCategory())) { - library.setCategory("Uncategorized"); + InputStream indexIn = null; + try { + indexIn = new FileInputStream(indexFile); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new MrBeanModule()); + mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + index = mapper.readValue(indexIn, LibrariesIndex.class); + + for (ContributedLibrary library : index.getLibraries()) { + if (library.getCategory() == null || "".equals(library.getCategory())) { + library.setCategory("Uncategorized"); + } + } + } finally { + if (indexIn != null) { + indexIn.close(); } } } diff --git a/arduino-core/src/cc/arduino/contributions/packages/Constants.java b/arduino-core/src/cc/arduino/contributions/packages/Constants.java new file mode 100644 index 00000000000..1afbe00753d --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/packages/Constants.java @@ -0,0 +1,23 @@ +package cc.arduino.contributions.packages; + +import java.util.Arrays; +import java.util.List; + +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 PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS = "boardsmanager.additional.urls"; + + static { + String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL"); + if (extenalPackageIndexUrl != null && !"".equals(extenalPackageIndexUrl)) { + PACKAGE_INDEX_URL = extenalPackageIndexUrl; + } else { + PACKAGE_INDEX_URL = "http://downloads.arduino.cc/packages/package_index.json"; + } + } + +} diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java b/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java index 32de99aa423..83565f91283 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributedPlatform.java @@ -39,6 +39,8 @@ public abstract class ContributedPlatform extends DownloadableContribution { public abstract String getCategory(); + public abstract void setCategory(String category); + public abstract String getArchitecture(); public abstract String getChecksum(); diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java index 05259cdfdcd..4abd4801a5c 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionInstaller.java @@ -28,6 +28,7 @@ */ package cc.arduino.contributions.packages; +import cc.arduino.contributions.GPGDetachedSignatureVerifier; import cc.arduino.utils.ArchiveExtractor; import cc.arduino.utils.MultiStepProgress; import cc.arduino.utils.Progress; @@ -36,6 +37,8 @@ import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.Executor; import processing.app.BaseNoGui; +import processing.app.I18n; +import processing.app.PreferencesData; import processing.app.helpers.FileUtils; import processing.app.helpers.filefilters.OnlyDirs; import processing.app.tools.CollectStdOutStdErrExecutor; @@ -44,27 +47,13 @@ import java.io.File; import java.io.IOException; import java.net.URL; -import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; +import java.util.*; import static processing.app.I18n._; import static processing.app.I18n.format; public class ContributionInstaller { - private static final String PACKAGE_INDEX_URL; - - static { - String extenalPackageIndexUrl = System.getProperty("PACKAGE_INDEX_URL"); - if (extenalPackageIndexUrl != null && !"".equals(extenalPackageIndexUrl)) { - PACKAGE_INDEX_URL = extenalPackageIndexUrl; - } else { - PACKAGE_INDEX_URL = "http://downloads.arduino.cc/packages/package_index.json"; - } - } - private final ContributionsIndexer indexer; private final DownloadableContributionsDownloader downloader; @@ -239,29 +228,80 @@ public List remove(ContributedPlatform platform) { } public List updateIndex() throws Exception { - List errors = new LinkedList(); MultiStepProgress progress = new MultiStepProgress(1); - String statusText = _("Downloading platforms index..."); - URL url = new URL(PACKAGE_INDEX_URL); - File outputFile = indexer.getIndexFile(); - File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp"); - downloader.download(url, tmpFile, progress, statusText); + List downloadedPackageIndexFilesAccumulator = new LinkedList(); + downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, Constants.PACKAGE_INDEX_URL); + + Set packageIndexURLs = new HashSet(); + String additionalURLs = PreferencesData.get(Constants.PREFERENCES_BOARDS_MANAGER_ADDITIONAL_URLS, ""); + if (!"".equals(additionalURLs)) { + packageIndexURLs.addAll(Arrays.asList(additionalURLs.split(","))); + } + + for (String packageIndexURL : packageIndexURLs) { + downloadIndexAndSignature(progress, downloadedPackageIndexFilesAccumulator, packageIndexURL); + } + progress.stepDone(); - // TODO: Check downloaded index + return downloadedPackageIndexFilesAccumulator; + } + + private void downloadIndexAndSignature(MultiStepProgress progress, List downloadedPackagedIndexFilesAccumulator, String packageIndexUrl) throws Exception { + File packageIndex = download(progress, packageIndexUrl); + downloadedPackagedIndexFilesAccumulator.add(packageIndex.getName()); + try { + File packageIndexSignature = download(progress, packageIndexUrl + ".sig"); + boolean signatureVerified = new GPGDetachedSignatureVerifier().verify(packageIndex, packageIndexSignature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + if (signatureVerified) { + downloadedPackagedIndexFilesAccumulator.add(packageIndexSignature.getName()); + } else { + downloadedPackagedIndexFilesAccumulator.remove(packageIndex.getName()); + packageIndex.delete(); + packageIndexSignature.delete(); + System.err.println(I18n.format(_("{0} file signature verification failed. File ignored."), packageIndexUrl)); + } + } catch (Exception e) { + //ignore errors + } + } + + private File download(MultiStepProgress progress, String packageIndexUrl) throws Exception { + String statusText = _("Downloading platforms index..."); + URL url = new URL(packageIndexUrl); + String[] urlPathParts = url.getFile().split("/"); + File outputFile = indexer.getIndexFile(urlPathParts[urlPathParts.length - 1]); + File tmpFile = new File(outputFile.getAbsolutePath() + ".tmp"); + downloader.download(url, tmpFile, progress, statusText); // Replace old index with the updated one if (outputFile.exists()) { - outputFile.delete(); + if (!outputFile.delete()) { + throw new Exception("An error occurred while updating platforms index! I can't delete file " + outputFile); + } } if (!tmpFile.renameTo(outputFile)) { - throw new Exception("An error occurred while updating platforms index!"); + throw new Exception("An error occurred while updating platforms index! I can't rename file " + tmpFile); } - return errors; + + return outputFile; } protected void onProgress(Progress progress) { // Empty } + + public void deleteUnknownFiles(List downloadedPackageIndexFiles) { + File preferencesFolder = indexer.getIndexFile(".").getParentFile(); + File[] additionalPackageIndexFiles = preferencesFolder.listFiles(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME)); + if (additionalPackageIndexFiles == null) { + return; + } + for (File additionalPackageIndexFile : additionalPackageIndexFiles) { + if (!downloadedPackageIndexFiles.contains(additionalPackageIndexFile.getName())) { + additionalPackageIndexFile.delete(); + } + } + } } diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java index 6cb4391f6fb..ecb8fad64e7 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndex.java @@ -137,10 +137,12 @@ public void fillCategories() { } } - public ContributedPackage getPackage(String packager) { - for (ContributedPackage pack : getPackages()) - if (pack.getName().equals(packager)) + public ContributedPackage getPackage(String packageName) { + for (ContributedPackage pack : getPackages()) { + if (pack.getName().equals(packageName)) { return pack; + } + } return null; } diff --git a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java index 6e73261620b..fd26b7e68c1 100644 --- a/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java +++ b/arduino-core/src/cc/arduino/contributions/packages/ContributionsIndexer.java @@ -29,6 +29,8 @@ package cc.arduino.contributions.packages; import cc.arduino.contributions.DownloadableContributionBuiltInAtTheBottomComparator; +import cc.arduino.contributions.GPGDetachedSignatureVerifier; +import cc.arduino.contributions.SignatureVerificationFailedException; import cc.arduino.contributions.filters.BuiltInPredicate; import cc.arduino.contributions.filters.InstalledPredicate; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -39,6 +41,7 @@ import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Multimaps; +import processing.app.BaseNoGui; import processing.app.debug.TargetPackage; import processing.app.debug.TargetPlatform; import processing.app.debug.TargetPlatformException; @@ -56,33 +59,28 @@ public class ContributionsIndexer { private final File packagesFolder; private final File stagingFolder; - private final File indexFile; + private final File preferencesFolder; private ContributionsIndex index; public ContributionsIndexer(File preferencesFolder) { + this.preferencesFolder = preferencesFolder; packagesFolder = new File(preferencesFolder, "packages"); - stagingFolder = new File(preferencesFolder, "staging" + File.separator + - "packages"); - indexFile = new File(preferencesFolder, "package_index.json"); + stagingFolder = new File(preferencesFolder, "staging" + File.separator + "packages"); } - // public static void main(String args[]) throws Exception { - // File indexFile = new File(args[0]); - // - // // VerifyResult verify = ClearSignedVerifier.verify(indexFile, - // // new PackagersPublicKeys()); - // // if (!verify.verified) - // // throw new Exception("Invalid index file!"); - // - // ContributionsIndexer indexer = new ContributionsIndexer(null); - // // indexer.parse(new ByteArrayInputStream(verify.clearText)); - // indexer.parseIndex(indexFile); - // indexer.syncWithFilesystem(); - // } - - public void parseIndex() throws IOException { - // Parse index file - parseIndex(indexFile); + public void parseIndex() throws Exception { + File defaultIndexFile = getIndexFile(Constants.DEFAULT_INDEX_FILE_NAME); + if (!isSigned(defaultIndexFile)) { + throw new SignatureVerificationFailedException(Constants.DEFAULT_INDEX_FILE_NAME); + } + index = parseIndex(defaultIndexFile); + + File[] indexFiles = preferencesFolder.listFiles(new TestPackageIndexFilenameFilter(new PackageIndexFilenameFilter(Constants.DEFAULT_INDEX_FILE_NAME))); + + for (File indexFile : indexFiles) { + ContributionsIndex contributionsIndex = parseIndex(indexFile); + mergeContributions(contributionsIndex, indexFile); + } List packages = index.getPackages(); for (ContributedPackage pack : packages) { @@ -98,14 +96,82 @@ public void parseIndex() throws IOException { index.fillCategories(); } - private void parseIndex(File indexFile) throws IOException { - InputStream indexIn = new FileInputStream(indexFile); - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new MrBeanModule()); - mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); - mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - index = mapper.readValue(indexIn, ContributionsIndex.class); + private void mergeContributions(ContributionsIndex contributionsIndex, File indexFile) { + boolean signed = isSigned(indexFile); + + for (ContributedPackage contributedPackage : contributionsIndex.getPackages()) { + if (!signed) { + for (ContributedPlatform contributedPlatform : contributedPackage.getPlatforms()) { + contributedPlatform.setCategory("Contributed"); + } + } + + ContributedPackage targetPackage = index.getPackage(contributedPackage.getName()); + + if (targetPackage == null) { + index.getPackages().add(contributedPackage); + } else { + if (signed || !isPackageNameProtected(contributedPackage)) { + List platforms = contributedPackage.getPlatforms(); + if (platforms == null) { + platforms = new LinkedList(); + } + for (ContributedPlatform contributedPlatform : platforms) { + ContributedPlatform platform = targetPackage.findPlatform(contributedPlatform.getArchitecture(), contributedPlatform.getVersion()); + if (platform != null) { + targetPackage.getPlatforms().remove(platform); + } + targetPackage.getPlatforms().add(contributedPlatform); + } + List tools = contributedPackage.getTools(); + if (tools == null) { + tools = new LinkedList(); + } + for (ContributedTool contributedTool : tools) { + ContributedTool tool = targetPackage.findTool(contributedTool.getName(), contributedTool.getVersion()); + if (tool != null) { + targetPackage.getTools().remove(tool); + } + targetPackage.getTools().add(contributedTool); + } + } + } + } + } + + private boolean isPackageNameProtected(ContributedPackage contributedPackage) { + return Constants.PROTECTED_PACKAGE_NAMES.contains(contributedPackage.getName()); + } + + private boolean isSigned(File indexFile) { + File signature = new File(indexFile.getParent(), indexFile.getName() + ".sig"); + if (!signature.exists()) { + return false; + } + + try { + return new GPGDetachedSignatureVerifier().verify(indexFile, signature, new File(BaseNoGui.getContentFile("lib"), "public.gpg.key")); + } catch (Exception e) { + BaseNoGui.showWarning(e.getMessage(), e.getMessage(), e); + return false; + } + } + + private ContributionsIndex parseIndex(File indexFile) throws IOException { + InputStream inputStream = null; + try { + inputStream = new FileInputStream(indexFile); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new MrBeanModule()); + mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.configure(DeserializationFeature.EAGER_DESERIALIZER_FETCH, true); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + return mapper.readValue(inputStream, ContributionsIndex.class); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } } public void syncWithFilesystem(File hardwareFolder) throws IOException { @@ -115,6 +181,9 @@ public void syncWithFilesystem(File hardwareFolder) throws IOException { } public void syncBuiltInHardwareFolder(File hardwareFolder) throws IOException { + if (index == null) { + return; + } for (File folder : hardwareFolder.listFiles(ONLY_DIRS)) { ContributedPackage pack = index.findPackage(folder.getName()); if (pack != null) { @@ -149,8 +218,13 @@ private void syncBuiltInPackageWithFilesystem(ContributedPackage pack, File hard } public void syncLocalPackagesFolder() { - if (!packagesFolder.isDirectory()) + if (!packagesFolder.isDirectory()) { + return; + } + + if (index == null) { return; + } // Scan all hardware folders and mark as installed all the // platforms found. @@ -216,6 +290,10 @@ public String toString() { public List createTargetPackages() throws TargetPlatformException { List packages = new ArrayList(); + if (index == null) { + return packages; + } + for (ContributedPackage aPackage : index.getPackages()) { ContributedTargetPackage targetPackage = new ContributedTargetPackage(aPackage.getName()); @@ -261,6 +339,9 @@ public boolean isContributedToolUsed(ContributedTool tool) { public Set getInstalledTools() { Set tools = new HashSet(); + if (index == null) { + return tools; + } for (ContributedPackage pack : index.getPackages()) { Collection platforms = Collections2.filter(pack.getPlatforms(), new InstalledPredicate()); ImmutableListMultimap platformsByName = Multimaps.index(platforms, new Function() { @@ -295,8 +376,28 @@ public File getStagingFolder() { return stagingFolder; } - public File getIndexFile() { - return indexFile; + public File getIndexFile(String name) { + return new File(preferencesFolder, name); + } + + public List getPackages() { + if (index == null) { + return new LinkedList(); + } + return index.getPackages(); } + public List getCategories() { + if (index == null) { + return new LinkedList(); + } + return index.getCategories(); + } + + public ContributedPlatform getInstalled(String packageName, String platformArch) { + if (index == null) { + return null; + } + return index.getInstalled(packageName, platformArch); + } } diff --git a/arduino-core/src/cc/arduino/contributions/packages/PackageIndexFilenameFilter.java b/arduino-core/src/cc/arduino/contributions/packages/PackageIndexFilenameFilter.java new file mode 100644 index 00000000000..61e7c2663c0 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/packages/PackageIndexFilenameFilter.java @@ -0,0 +1,18 @@ +package cc.arduino.contributions.packages; + +import java.io.File; +import java.io.FilenameFilter; + +public class PackageIndexFilenameFilter implements FilenameFilter { + + private final String defaultPackageIndexFileName; + + public PackageIndexFilenameFilter(String defaultPackageIndexFileName) { + this.defaultPackageIndexFileName = defaultPackageIndexFileName; + } + + @Override + public boolean accept(File file, String name) { + return new File(file, name).isFile() && !defaultPackageIndexFileName.equals(name) && name.startsWith("package_") && name.endsWith("_index.json"); + } +} diff --git a/arduino-core/src/cc/arduino/contributions/packages/TestPackageIndexFilenameFilter.java b/arduino-core/src/cc/arduino/contributions/packages/TestPackageIndexFilenameFilter.java new file mode 100644 index 00000000000..6d134eb7791 --- /dev/null +++ b/arduino-core/src/cc/arduino/contributions/packages/TestPackageIndexFilenameFilter.java @@ -0,0 +1,27 @@ +package cc.arduino.contributions.packages; + +import java.io.File; +import java.io.FilenameFilter; + +public class TestPackageIndexFilenameFilter implements FilenameFilter { + + private final FilenameFilter parent; + + public TestPackageIndexFilenameFilter(FilenameFilter parent) { + this.parent = parent; + } + + public TestPackageIndexFilenameFilter() { + this(null); + } + + @Override + public boolean accept(File file, String name) { + boolean result = false; + if (parent != null) { + result = parent.accept(file, name); + } + result = result || (new File(file, name).isFile() && name.startsWith("test_package_") && name.endsWith("_index.json")); + return result; + } +} diff --git a/arduino-core/src/cc/arduino/packages/security/ClearSignedVerifier.java b/arduino-core/src/cc/arduino/packages/security/ClearSignedVerifier.java deleted file mode 100644 index 0c53126a93c..00000000000 --- a/arduino-core/src/cc/arduino/packages/security/ClearSignedVerifier.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 2013 Arduino LLC (http://www.arduino.cc/) - */ - -package cc.arduino.packages.security; - -import cc.arduino.packages.security.keys.PackagersPublicKeys; -import org.bouncycastle.bcpg.ArmoredInputStream; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openpgp.*; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; -import processing.app.helpers.StringUtils; - -import java.io.*; -import java.security.Security; - -public class ClearSignedVerifier { - - public static class VerifyResult { - public byte clearText[]; - public boolean verified; - public Exception error; - } - - /** - * Verify a PGP clearText-signature. - * - * @param signedTextFile A File containing the clearText signature - * @param pubKeyRing A public key-ring containing the public key needed for the - * signature verification - * @return A VerifyResult class with the clearText and the signature - * verification status - * @throws FileNotFoundException - */ - public static VerifyResult verify(File signedTextFile, - PGPPublicKeyRingCollection pubKeyRing) { - // Create the result object - VerifyResult result = new VerifyResult(); - result.clearText = null; - result.verified = false; - result.error = null; - - ArmoredInputStream in = null; - try { - // Extract clear text. - // Dash-encoding is removed by ArmoredInputStream. - in = new ArmoredInputStream(new FileInputStream(signedTextFile)); - ByteArrayOutputStream temp = new ByteArrayOutputStream(in.available()); - while (true) { - int c = in.read(); - if (c == -1) - throw new IOException("Unexpected end of file"); - if (!in.isClearText()) - break; - temp.write(c); - } - byte clearText[] = temp.toByteArray(); - result.clearText = clearText; - - // Extract signature from clear-signed text - PGPObjectFactory pgpFact = new PGPObjectFactory(in); - PGPSignatureList p3 = (PGPSignatureList) pgpFact.nextObject(); - PGPSignature sig = p3.get(0); - - // Decode public key - PGPPublicKey publicKey = pubKeyRing.getPublicKey(sig.getKeyID()); - - // Verify signature - Security.addProvider(new BouncyCastleProvider()); - sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider("BC"), - publicKey); - // RFC 4880, section 7: http://tools.ietf.org/html/rfc4880#section-7 - // The signature must be validated using clear text: - // - without trailing white spaces on every line - // - using CR LF line endings, no matter what the original line ending is - // - without the latest line ending - BufferedReader textIn = new BufferedReader(new InputStreamReader( - new ByteArrayInputStream(clearText))); - while (true) { - // remove trailing whitespace and line endings - String line = StringUtils.rtrim(textIn.readLine()); - sig.update(line.getBytes()); - if (!textIn.ready()) // skip latest line ending - break; - // always use CR LF - sig.update((byte) '\r'); - sig.update((byte) '\n'); - } - - // Prepare the result - result.verified = sig.verify(); - } catch (Exception e) { - result.error = e; - } finally { - if (in != null) - try { - in.close(); - } catch (IOException e) { - // ignored - } - } - return result; - } - - public static void main(String[] args) throws Exception { - VerifyResult verify = verify(new File( - "/home/megabug/git/arduino/test.txt.asc"), new PackagersPublicKeys()); - System.out.println(verify.verified); - } -} diff --git a/arduino-core/src/cc/arduino/packages/security/keys/PackagersPublicKeys.java b/arduino-core/src/cc/arduino/packages/security/keys/PackagersPublicKeys.java deleted file mode 100644 index 49c419bc611..00000000000 --- a/arduino-core/src/cc/arduino/packages/security/keys/PackagersPublicKeys.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 2013 Arduino LLC (http://www.arduino.cc/) - */ - -package cc.arduino.packages.security.keys; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKeyRingCollection; -import org.bouncycastle.openpgp.PGPUtil; - -public class PackagersPublicKeys extends PGPPublicKeyRingCollection { - - public static final String ARDUINO_PK = "" // - + "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" - + "Version: GnuPG v1.4.11 (GNU/Linux)\n" - + "\n" - + "mQINBFJ9IskBEAD3vGGYFl+gib5WURZwcW7e1Z2+ZAd48LP+KsZ2RVHv7FhzsH1s\n" - + "eSRNsuLUXV0DHTCGUUqvQYV/+HLnv4hQvRFogql5zapQldS4mhWO0jcVuee3lDun\n" - + "nmQEN3Ikn0Zf2+sQD0iMXk8eRz3MJx2xDs7yK3ZHkdkie7pqNKg6yrJ64x5H3HJn\n" - + "y7aOSN3ClNgTbxdNlfCQfhex12c+fiOqVO4f5KyYvRo3zBMv8TV4JL0KG1L+uuKU\n" - + "uuXyG4jUhldpf+1fazX3bW0432rfnxNI2JsPCQ5h95nQNrW0LRS1Nrts8UTePt/9\n" - + "trJ1sIlSMRyG7mkq3gzTf4a2BaUejHHNGLwXBBMyGNQqei+hawwnimlD7egXKpa3\n" - + "uy8La2rB37RK672CjsN2KSOU7B6UpTQY6VCjkC0yQY6u9Kp8P9XY5M6HIZhBwVpk\n" - + "kPfJ93b73leMcSDSU6cCcCdWpkCUDQNpBLa4k0vr4nEC5hs8Q6RjpgVgGDulY2Xf\n" - + "hWkrh430r+a50wbEmSZwPg05wnC0n2pu+hpSF7mNx4FhnfXQ3eaJHvW/4hCdwxAg\n" - + "tbC+yXPmEJ01h3cK53xI8Usve4pizaxb2FuMf5FmOTt/B/H+KmHAOLcY3xCMxF9t\n" - + "wcXVHdfkWfZk4LK2RUo+oe3Z2SXSGuOj61pP5wnvRYojtURwjrehArTrpwARAQAB\n" - + "tCZBcmR1aW5vIFBhY2thZ2VzIDxwYWNrYWdlc0BhcmR1aW5vLmNjPokCPgQTAQIA\n" - + "KAUCUn0iyQIbAwUJCWYBgAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQPrLD\n" - + "cgsG1iRL7A/8Cf/S6xgksnSk1GD+6tSLEZHku7tLEhiCX38pS8a6UBAj1UGrbxPn\n" - + "kS0iPLcfJG7AblI4EjrYTMaLHUL0UtcOvq8+F9/NrZAVW6+xOpm9LTQhUMh+ddCx\n" - + "6igY3BRr9WtNkrowzMUGpGrJzIw7v3hiJbXDNIpTwoLF4aFEgOJXyJZawp8rjBOw\n" - + "bnRlq9MTC/7+nm/d7i7nsRxDyGcZllVDIFI38sVVmLL8eBd7z9Vn1RsZDkPzhGzM\n" - + "aKdFCU7hD15H3hPkeidkbLwmd5jihZCDDd2LhGtMFo3cwqouKo/UTaYomNP2iGlU\n" - + "2BBpuQTPcJy2d1d8lCr19yARcJCVC9H6sISkXPHO0EMgGrpdFgxDVGeEkvn1scHV\n" - + "x+k4BCOMl1WfsD4JkAJ5RyyZNjP0oDlY8SROfT479Ypb6qLMGCOujxjHapv/t2PH\n" - + "2cjadiMP62AN3rIiMTZWpCgL+bu3j8hsbAnlaINxoiab72+8uuA53o2SKWIA4J24\n" - + "0wu7ETW0QQkxym/ammX/nXgap/R9u/A8kGx+QKPyjptO+hnc7vgGAMEIrVDsKeTp\n" - + "AmIwtxvK3AIKGkup+E+ee2kzBhJHhsoDpmJZgaIxoiCdOZglaA+V53I16Vv+fiC1\n" - + "SW9cN5UQvlNycu8QFQwwz/Eg7M8abQDXBgf6znAKt0wSn6tI/b/NBmG5Ag0EUn0i\n" - + "yQEQAK8ZvoX51FizIt49nfwWR6w7CCG35B92oVTKn1oLPSF9fU75dmBd57sFAle0\n" - + "Zm5DzfzCQ1d6voo8HhmUQHIE1NamC1YE6c2AKvc4xx4ltjjPqi8+KJ1y5gNz1M5Q\n" - + "ZRnzjxjkCePSRxQXoEDNINryPvNQLzrFbtm5R2tsygwqaVxhJok4hny1srhxd8IZ\n" - + "rz5MBlRtRr31D494GRD4iSKyvpAC+zh2ZL1+zUtg7qQU0FybUJ/hIJ2DRHNwuutp\n" - + "2EzbDwJJNNDjjNC8NKdJ4GgOJJnKGU52OfdmeEeI1+wDm3/FvI4lSS9O/ay4ty3/\n" - + "wSwhGKOWNXowGFVXdzxYyCOf1NVDHn8Vj8sU2lHw5Fn2RA41xAs33aLPjLmdv7xa\n" - + "7mJSp0cfiudPyVWP0D+/269kMq6W3V9IFeRQMxObNXWACKzuaaLi60ncGaXvM/q1\n" - + "2O0HvQ9+BlLE7DSQWGb8NTngSAFYUYdWZ1GhiyTvFKkSDIAhkQfYLc0Kky6y1D2J\n" - + "w0alVMdroHwf67V+ya2+Ac8EGv0oQvAF7ug1Ymnjx59kqV6IxdsPdRAmfZT63yJS\n" - + "C6ZDEbuqP3SUCehSwO/GW0Echwuga87At4RJ6OQ8HxdhjFMGjQANp+He6L7O2dav\n" - + "+JbH1687fc65VO8sTbhfW6Ntzr/MIVdS6rc1RzHMfMeVcuFJABEBAAGJAiUEGAEC\n" - + "AA8FAlJ9IskCGwwFCQlmAYAACgkQPrLDcgsG1iRQwg//VhFjyz1q/jxB7HbUEGhT\n" - + "wNsT5lOVXIJy8Y6CyAQLjI5LatZxMdIqZOlkPgHiMpMqJqvDgBgR/44UKL4yzvmv\n" - + "/6DIeMngej2oD794Q4j4LlnQopolvZy7dSyQqWX3kqEABAPMYnLhR9r/PQPiienR\n" - + "E7p1+zC/czcpL7wHKChjPgegPDrJ7yOug9IgwFSfokF5BFR3aNJNOxEHd+YSXfS4\n" - + "i4Eef3YovQfUvMfq7jux7Qsi8igzvm782pPsylPwysd/d0emlkGqMLGHWh+r1eIy\n" - + "UzOXgfhwgN38RB/p1joVSZGpmTu6y9e50HME9FrYEmCrNwYTOi1fQB/IHr7lg1qT\n" - + "/Bap938b6vm08pEDnVLSahsgdJdG8YYwJEg2BJnpThIGHnle9Ahmk3OMI7Wl9VsQ\n" - + "1MJ+va/rWjKvq6z7k0YzQbrJStrlrErbi4DN0YTmNV2M6IDcySjhCSAww7nqHiIx\n" - + "sJGggMBQS0/KQCsyXHeLpJwoSTv16c9UajV7/wJA8r7yNZV9a/5LrC2hRoN4RnU5\n" - + "kN//5xNON5L5Qd40XslUaFv4J/f21xuLgCkDb9N/jqwq7gLkkP/1WX8UkmWLvGM0\n" - + "J5DkabHzgusefEG9pNsFwExzAg4IFYKgG2qbS0zNQV8uMUD9vF7K/6YZgcBjH5gc\n" - + "KCcKZZVUQLJhOIwgHQMy7ck=\n" // - + "=u0/X\n" // - + "-----END PGP PUBLIC KEY BLOCK-----\n"; - - public PackagersPublicKeys() throws IOException, PGPException { - super(PGPUtil.getDecoderStream(new ByteArrayInputStream(ARDUINO_PK.getBytes()))); - } - -} diff --git a/arduino-core/src/cc/arduino/utils/ArchiveExtractor.java b/arduino-core/src/cc/arduino/utils/ArchiveExtractor.java index 8e4ed800d15..fe68fa8707e 100644 --- a/arduino-core/src/cc/arduino/utils/ArchiveExtractor.java +++ b/arduino-core/src/cc/arduino/utils/ArchiveExtractor.java @@ -91,19 +91,13 @@ public void extract(File archiveFile, File destFolder, int stripPath, boolean ov // Create an ArchiveInputStream with the correct archiving algorithm if (archiveFile.getName().endsWith("tar.bz2")) { - InputStream fin = new FileInputStream(archiveFile); - fin = new BZip2CompressorInputStream(fin); - in = new TarArchiveInputStream(fin); + in = new TarArchiveInputStream(new BZip2CompressorInputStream(new FileInputStream(archiveFile))); } else if (archiveFile.getName().endsWith("zip")) { - InputStream fin = new FileInputStream(archiveFile); - in = new ZipArchiveInputStream(fin); + in = new ZipArchiveInputStream(new FileInputStream(archiveFile)); } else if (archiveFile.getName().endsWith("tar.gz")) { - InputStream fin = new FileInputStream(archiveFile); - fin = new GzipCompressorInputStream(fin); - in = new TarArchiveInputStream(fin); + in = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(archiveFile))); } else if (archiveFile.getName().endsWith("tar")) { - InputStream fin = new FileInputStream(archiveFile); - in = new TarArchiveInputStream(fin); + in = new TarArchiveInputStream(new FileInputStream(archiveFile)); } else { throw new IOException("Archive format not supported."); } @@ -276,8 +270,9 @@ public void extract(File archiveFile, File destFolder, int stripPath, boolean ov } private static void copyStreamToFile(InputStream in, long size, File outputFile) throws IOException { - FileOutputStream fos = new FileOutputStream(outputFile); + FileOutputStream fos = null; try { + fos = new FileOutputStream(outputFile); // if size is not available, copy until EOF... if (size == -1) { byte buffer[] = new byte[4096]; @@ -299,7 +294,9 @@ private static void copyStreamToFile(InputStream in, long size, File outputFile) size -= length; } } finally { - fos.close(); + if (fos != null) { + fos.close(); + } } } diff --git a/arduino-core/src/cc/arduino/utils/FileHash.java b/arduino-core/src/cc/arduino/utils/FileHash.java index 87dabe17255..0ce8afd4cf0 100644 --- a/arduino-core/src/cc/arduino/utils/FileHash.java +++ b/arduino-core/src/cc/arduino/utils/FileHash.java @@ -40,19 +40,18 @@ public class FileHash { * Calculate a message digest of a file using the algorithm specified. The * result is a string containing the algorithm name followed by ":" and by the * resulting hash in hex. - * + * * @param file - * @param algorithm - * For example "SHA-256" + * @param algorithm For example "SHA-256" * @return The algorithm followed by ":" and the hash, for example:
- * "SHA-256:ee6796513086080cca078cbb383f543c5e508b647a71c9d6f39b7bca41071883" + * "SHA-256:ee6796513086080cca078cbb383f543c5e508b647a71c9d6f39b7bca41071883" * @throws IOException * @throws NoSuchAlgorithmException */ - public static String hash(File file, String algorithm) throws IOException, - NoSuchAlgorithmException { - FileInputStream in = new FileInputStream(file); + public static String hash(File file, String algorithm) throws IOException, NoSuchAlgorithmException { + FileInputStream in = null; try { + in = new FileInputStream(file); byte buff[] = new byte[10240]; MessageDigest digest = MessageDigest.getInstance(algorithm); while (in.available() > 0) { @@ -69,7 +68,9 @@ public static String hash(File file, String algorithm) throws IOException, } return algorithm + ":" + res; } finally { - in.close(); + if (in != null) { + in.close(); + } } } } diff --git a/arduino-core/src/processing/app/BaseNoGui.java b/arduino-core/src/processing/app/BaseNoGui.java index 285ab1ba67d..06e807fbe04 100644 --- a/arduino-core/src/processing/app/BaseNoGui.java +++ b/arduino-core/src/processing/app/BaseNoGui.java @@ -1,5 +1,6 @@ package processing.app; +import cc.arduino.contributions.SignatureVerificationFailedException; import cc.arduino.contributions.libraries.LibrariesIndexer; import cc.arduino.contributions.packages.ContributedTool; import cc.arduino.contributions.packages.ContributionsIndexer; @@ -232,13 +233,6 @@ static public List getLibrariesPath() { return librariesFolders; } - /** - * Return an InputStream for a file inside the Processing lib folder. - */ - static public InputStream getLibStream(String filename) throws IOException { - return new FileInputStream(new File(getContentFile("lib"), filename)); - } - static public Platform getPlatform() { return platform; } @@ -577,7 +571,7 @@ static public void initLogger() { static public void initPackages() throws Exception { indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder()); - File indexFile = indexer.getIndexFile(); + File indexFile = indexer.getIndexFile("package_index.json"); File defaultPackageJsonFile = new File(getContentFile("dist"), "package_index.json"); if (!indexFile.isFile() || (defaultPackageJsonFile.isFile() && defaultPackageJsonFile.lastModified() > indexFile.lastModified())) { FileUtils.copyFile(defaultPackageJsonFile, indexFile); @@ -594,7 +588,20 @@ static public void initPackages() throws Exception { } } } - indexer.parseIndex(); + + File indexSignatureFile = indexer.getIndexFile("package_index.json.sig"); + File defaultPackageJsonSignatureFile = new File(getContentFile("dist"), "package_index.json.sig"); + if (!indexSignatureFile.isFile() || (defaultPackageJsonSignatureFile.isFile() && defaultPackageJsonSignatureFile.lastModified() > indexSignatureFile.lastModified())) { + FileUtils.copyFile(defaultPackageJsonSignatureFile, indexSignatureFile); + } + + try { + indexer.parseIndex(); + } catch (SignatureVerificationFailedException e) { + indexFile.delete(); + indexSignatureFile.delete(); + throw e; + } indexer.syncWithFilesystem(getHardwareFolder()); packages = new HashMap(); @@ -610,13 +617,17 @@ static public void initPackages() throws Exception { if (defaultLibraryJsonFile.isFile()) { FileUtils.copyFile(defaultLibraryJsonFile, librariesIndexFile); } else { + FileOutputStream out = null; try { // Otherwise create an empty packages index - FileOutputStream out = new FileOutputStream(librariesIndexFile); + out = new FileOutputStream(librariesIndexFile); out.write("{ \"libraries\" : [ ] }".getBytes()); - out.close(); } catch (IOException e) { e.printStackTrace(); + } finally { + if (out != null) { + out.close(); + } } } } diff --git a/arduino-core/src/processing/app/PreferencesData.java b/arduino-core/src/processing/app/PreferencesData.java index b346299830f..d6dc1467911 100644 --- a/arduino-core/src/processing/app/PreferencesData.java +++ b/arduino-core/src/processing/app/PreferencesData.java @@ -45,7 +45,7 @@ static public void init(File file) { // start by loading the defaults, in case something // important was deleted from the user prefs try { - prefs.load(BaseNoGui.getLibStream(PREFS_FILE)); + prefs.load(new File(BaseNoGui.getContentFile("lib"), PREFS_FILE)); } catch (IOException e) { BaseNoGui.showError(null, _("Could not read default settings.\n" + "You'll need to reinstall Arduino."), e); @@ -94,41 +94,6 @@ private static void fixPreferences() { } - static public String[] loadStrings(InputStream input) { - try { - BufferedReader reader = - new BufferedReader(new InputStreamReader(input, "UTF-8")); - - String lines[] = new String[100]; - int lineCount = 0; - String line = null; - while ((line = reader.readLine()) != null) { - if (lineCount == lines.length) { - String temp[] = new String[lineCount << 1]; - System.arraycopy(lines, 0, temp, 0, lineCount); - lines = temp; - } - lines[lineCount++] = line; - } - reader.close(); - - if (lineCount == lines.length) { - return lines; - } - - // resize array to appropriate amount for these lines - String output[] = new String[lineCount]; - System.arraycopy(lines, 0, output, 0, lineCount); - return output; - - } catch (IOException e) { - e.printStackTrace(); - //throw new RuntimeException("Error inside loadStrings()"); - } - return null; - } - - static protected void save() { if (!doSave) return; @@ -139,18 +104,24 @@ static protected void save() { if (preferencesFile == null) return; // Fix for 0163 to properly use Unicode when writing preferences.txt - PrintWriter writer = PApplet.createWriter(preferencesFile); - - String[] keys = prefs.keySet().toArray(new String[0]); - Arrays.sort(keys); - for (String key: keys) { - if (key.startsWith("runtime.")) - continue; - writer.println(key + "=" + prefs.get(key)); - } + PrintWriter writer = null; + try { + writer = PApplet.createWriter(preferencesFile); + + String[] keys = prefs.keySet().toArray(new String[0]); + Arrays.sort(keys); + for (String key : keys) { + if (key.startsWith("runtime.")) + continue; + writer.println(key + "=" + prefs.get(key)); + } - writer.flush(); - writer.close(); + writer.flush(); + } finally { + if (writer != null) { + writer.close(); + } + } try { BaseNoGui.getPlatform().fixPrefsFilePermissions(preferencesFile); diff --git a/arduino-core/src/processing/app/debug/Compiler.java b/arduino-core/src/processing/app/debug/Compiler.java index e1c2e2fb24a..1bc0f907479 100644 --- a/arduino-core/src/processing/app/debug/Compiler.java +++ b/arduino-core/src/processing/app/debug/Compiler.java @@ -1210,12 +1210,12 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru // 2. run preproc on that code using the sugg class name // to create a single .java file and write to buildpath + FileOutputStream outputStream = null; try { // Output file File streamFile = new File(buildPath, sketch.getName() + ".cpp"); - FileOutputStream outputStream = new FileOutputStream(streamFile); + outputStream = new FileOutputStream(streamFile); preprocessor.write(outputStream); - outputStream.close(); } catch (FileNotFoundException fnfe) { fnfe.printStackTrace(); String msg = _("Build folder disappeared or could not be written"); @@ -1230,6 +1230,14 @@ public void preprocess(String buildPath, PdePreprocessor preprocessor) throws Ru System.err.println(I18n.format(_("Uncaught exception type: {0}"), ex.getClass())); ex.printStackTrace(); throw new RunnerException(ex.toString()); + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + //noop + } + } } // grab the imports from the code just preproc'd diff --git a/arduino-core/src/processing/app/helpers/PreferencesMap.java b/arduino-core/src/processing/app/helpers/PreferencesMap.java index f26c2b122e6..185e1bee49a 100644 --- a/arduino-core/src/processing/app/helpers/PreferencesMap.java +++ b/arduino-core/src/processing/app/helpers/PreferencesMap.java @@ -21,18 +21,14 @@ */ package processing.app.helpers; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; +import processing.app.legacy.PApplet; + +import java.io.*; import java.util.LinkedHashMap; import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; -import processing.app.legacy.PApplet; - @SuppressWarnings("serial") public class PreferencesMap extends LinkedHashMap { @@ -71,7 +67,15 @@ public PreferencesMap() { * @throws IOException */ public void load(File file) throws IOException { - load(new FileInputStream(file)); + FileInputStream fileInputStream = null; + try { + fileInputStream = new FileInputStream(file); + load(fileInputStream); + } finally { + if (fileInputStream != null) { + fileInputStream.close(); + } + } } protected String processPlatformSuffix(String key, String suffix, boolean isCurrentPlatform) { diff --git a/arduino-core/src/processing/app/legacy/PApplet.java b/arduino-core/src/processing/app/legacy/PApplet.java index 59ca701e319..e899552632b 100644 --- a/arduino-core/src/processing/app/legacy/PApplet.java +++ b/arduino-core/src/processing/app/legacy/PApplet.java @@ -266,9 +266,20 @@ static public int[] parseInt(String what[], int missing) { } static public String[] loadStrings(File file) { - InputStream is = createInput(file); - if (is != null) return loadStrings(is); - return null; + InputStream is = null; + try { + is = createInput(file); + if (is != null) return loadStrings(is); + return null; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // noop + } + } + } } static public String[] loadStrings(InputStream input) { @@ -319,14 +330,29 @@ public void saveStrings(String filename, String strings[]) { static public void saveStrings(File file, String strings[]) { - saveStrings(createOutput(file), strings); + OutputStream outputStream = null; + try { + outputStream = createOutput(file); + saveStrings(outputStream, strings); + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + //noop + } + } + } } static public void saveStrings(OutputStream output, String strings[]) { PrintWriter writer = createWriter(output); - for (int i = 0; i < strings.length; i++) { - writer.println(strings[i]); + if (writer == null) { + return; + } + for (String string : strings) { + writer.println(string); } writer.flush(); writer.close(); diff --git a/build/build.xml b/build/build.xml index a99486e57da..b26cbfb8063 100644 --- a/build/build.xml +++ b/build/build.xml @@ -346,6 +346,8 @@ + + @@ -609,7 +611,7 @@ - + @@ -617,9 +619,11 @@ + + - + @@ -627,6 +631,8 @@ + + @@ -846,6 +852,8 @@ + + + + - - + + + + + + + + + + diff --git a/build/macosx/template.app/Contents/Info.plist b/build/macosx/template.app/Contents/Info.plist index e48146869fe..9c573c988ba 100755 --- a/build/macosx/template.app/Contents/Info.plist +++ b/build/macosx/template.app/Contents/Info.plist @@ -97,7 +97,7 @@ - $JAVAROOT/antlr.jar:$JAVAROOT/apple.jar:$JAVAROOT/arduino-core.jar:$JAVAROOT/bcpg-jdk15on-149.jar:$JAVAROOT/bcprov-jdk15on-149.jar:$JAVAROOT/commons-codec-1.7.jar:$JAVAROOT/commons-compress-1.8.jar:$JAVAROOT/commons-exec-1.1.jar:$JAVAROOT/commons-httpclient-3.1.jar:$JAVAROOT/commons-lang3-3.3.2.jar:$JAVAROOT/commons-logging-1.0.4.jar:$JAVAROOT/ecj.jar:$JAVAROOT/guava-18.0.jar:$JAVAROOT/jackson-annotations-2.2.3.jar:$JAVAROOT/jackson-core-2.2.3.jar:$JAVAROOT/jackson-databind-2.2.3.jar:$JAVAROOT/jackson-module-mrbean-2.2.3.jar:$JAVAROOT/java-semver-0.8.0.jar:$JAVAROOT/jmdns-3.4.1.jar:$JAVAROOT/jna.jar:$JAVAROOT/jsch-0.1.50.jar:$JAVAROOT/jssc-2.8.0.jar:$JAVAROOT/pde.jar:$JAVAROOT/quaqua.jar + $JAVAROOT/antlr.jar:$JAVAROOT/apple.jar:$JAVAROOT/arduino-core.jar:$JAVAROOT/bcpg-jdk15on-152.jar:$JAVAROOT/bcprov-jdk15on-152.jar:$JAVAROOT/commons-codec-1.7.jar:$JAVAROOT/commons-compress-1.8.jar:$JAVAROOT/commons-exec-1.1.jar:$JAVAROOT/commons-httpclient-3.1.jar:$JAVAROOT/commons-lang3-3.3.2.jar:$JAVAROOT/commons-logging-1.0.4.jar:$JAVAROOT/ecj.jar:$JAVAROOT/guava-18.0.jar:$JAVAROOT/jackson-annotations-2.2.3.jar:$JAVAROOT/jackson-core-2.2.3.jar:$JAVAROOT/jackson-databind-2.2.3.jar:$JAVAROOT/jackson-module-mrbean-2.2.3.jar:$JAVAROOT/java-semver-0.8.0.jar:$JAVAROOT/jmdns-3.4.1.jar:$JAVAROOT/jna.jar:$JAVAROOT/jsch-0.1.50.jar:$JAVAROOT/jssc-2.8.0.jar:$JAVAROOT/pde.jar:$JAVAROOT/quaqua.jar JVMArchs diff --git a/build/shared/lib/public.gpg.key b/build/shared/lib/public.gpg.key new file mode 100644 index 00000000000..5de39fed1f7 Binary files /dev/null and b/build/shared/lib/public.gpg.key differ diff --git a/build/windows/launcher/config.xml b/build/windows/launcher/config.xml index 30521fe9451..afc6e700841 100644 --- a/build/windows/launcher/config.xml +++ b/build/windows/launcher/config.xml @@ -18,8 +18,8 @@ lib/antlr.jar lib/apple.jar lib/arduino-core.jar - lib/bcpg-jdk15on-149.jar - lib/bcprov-jdk15on-149.jar + lib/bcpg-jdk15on-152.jar + lib/bcprov-jdk15on-152.jar lib/commons-codec-1.7.jar lib/commons-compress-1.8.jar lib/commons-exec-1.1.jar diff --git a/build/windows/launcher/config_debug.xml b/build/windows/launcher/config_debug.xml index 9731811f6db..88cbbb5b486 100644 --- a/build/windows/launcher/config_debug.xml +++ b/build/windows/launcher/config_debug.xml @@ -18,8 +18,8 @@ lib/antlr.jar lib/apple.jar lib/arduino-core.jar - lib/bcpg-jdk15on-149.jar - lib/bcprov-jdk15on-149.jar + lib/bcpg-jdk15on-152.jar + lib/bcprov-jdk15on-152.jar lib/commons-codec-1.7.jar lib/commons-compress-1.8.jar lib/commons-exec-1.1.jar