30
30
package cc .arduino .contributions ;
31
31
32
32
import cc .arduino .utils .FileHash ;
33
+ import cc .arduino .utils .MultiStepProgress ;
33
34
import cc .arduino .utils .Progress ;
34
35
import cc .arduino .utils .network .FileDownloader ;
36
+ import org .apache .commons .io .FilenameUtils ;
37
+ import org .apache .logging .log4j .LogManager ;
38
+ import org .apache .logging .log4j .Logger ;
39
+ import processing .app .BaseNoGui ;
40
+ import processing .app .PreferencesData ;
35
41
36
42
import java .io .File ;
37
43
import java .net .URL ;
38
- import java .nio .file .Files ;
39
- import java .nio .file .LinkOption ;
40
- import java .nio .file .Path ;
41
- import java .nio .file .Paths ;
44
+ import java .nio .file .*;
45
+ import java .util .Collection ;
42
46
43
47
import static processing .app .I18n .format ;
44
48
import static processing .app .I18n .tr ;
45
49
46
50
public class DownloadableContributionsDownloader {
51
+ private static Logger log = LogManager .getLogger (DownloadableContributionsDownloader .class );
47
52
48
53
private final File stagingFolder ;
49
54
50
55
public DownloadableContributionsDownloader (File _stagingFolder ) {
51
56
stagingFolder = _stagingFolder ;
52
57
}
53
58
54
- public File download (DownloadableContribution contribution , Progress progress , final String statusText , ProgressListener progressListener ) throws Exception {
55
- return download (contribution , progress , statusText , progressListener , false );
59
+ public File download (DownloadableContribution contribution , Progress progress , final String statusText , ProgressListener progressListener , boolean allowCache ) throws Exception {
60
+ return download (contribution , progress , statusText , progressListener , false , allowCache );
56
61
}
57
62
58
- public File download (DownloadableContribution contribution , Progress progress , final String statusText , ProgressListener progressListener , boolean noResume ) throws Exception {
63
+ public File download (DownloadableContribution contribution , Progress progress , final String statusText , ProgressListener progressListener , boolean noResume , boolean allowCache ) throws Exception {
59
64
URL url = new URL (contribution .getUrl ());
60
65
Path outputFile = Paths .get (stagingFolder .getAbsolutePath (), contribution .getArchiveFileName ());
61
66
@@ -70,7 +75,7 @@ public File download(DownloadableContribution contribution, Progress progress, f
70
75
while (true ) {
71
76
// Need to download or resume downloading?
72
77
if (!Files .isRegularFile (outputFile , LinkOption .NOFOLLOW_LINKS ) || (Files .size (outputFile ) < contribution .getSize ())) {
73
- download (url , outputFile .toFile (), progress , statusText , progressListener , noResume );
78
+ download (url , outputFile .toFile (), progress , statusText , progressListener , noResume , allowCache );
74
79
downloaded = true ;
75
80
}
76
81
@@ -116,12 +121,12 @@ private boolean hasChecksum(DownloadableContribution contribution) {
116
121
return algo != null && !algo .isEmpty ();
117
122
}
118
123
119
- public void download (URL url , File tmpFile , Progress progress , String statusText , ProgressListener progressListener ) throws Exception {
120
- download (url , tmpFile , progress , statusText , progressListener , false );
124
+ public void download (URL url , File tmpFile , Progress progress , String statusText , ProgressListener progressListener , boolean allowCache ) throws Exception {
125
+ download (url , tmpFile , progress , statusText , progressListener , false , allowCache );
121
126
}
122
127
123
- public void download (URL url , File tmpFile , Progress progress , String statusText , ProgressListener progressListener , boolean noResume ) throws Exception {
124
- FileDownloader downloader = new FileDownloader (url , tmpFile );
128
+ public void download (URL url , File tmpFile , Progress progress , String statusText , ProgressListener progressListener , boolean noResume , boolean allowCache ) throws Exception {
129
+ final FileDownloader downloader = new FileDownloader (url , tmpFile , allowCache );
125
130
downloader .addObserver ((o , arg ) -> {
126
131
FileDownloader me = (FileDownloader ) o ;
127
132
String msg = "" ;
@@ -140,4 +145,94 @@ public void download(URL url, File tmpFile, Progress progress, String statusText
140
145
}
141
146
}
142
147
148
+ public void downloadIndexAndSignature (MultiStepProgress progress , URL packageIndexUrl , ProgressListener progressListener , SignatureVerifier signatureVerifier ) throws Exception {
149
+
150
+ // Extract the file name from the url
151
+ final String indexFileName = FilenameUtils .getName (packageIndexUrl .getPath ());
152
+ final File packageIndex = BaseNoGui .indexer .getIndexFile (indexFileName );
153
+
154
+ final String statusText = tr ("Downloading platforms index..." );
155
+
156
+ // Create temp files
157
+ final File packageIndexTemp = File .createTempFile (indexFileName , ".tmp" );
158
+ try {
159
+ // Download package index
160
+ download (packageIndexUrl , packageIndexTemp , progress , statusText , progressListener , true , true );
161
+ final URL signatureUrl = new URL (packageIndexUrl .toString () + ".sig" );
162
+
163
+ if (verifyDomain (packageIndexUrl )) {
164
+ if (checkSignature (progress , signatureUrl , progressListener , signatureVerifier , statusText , packageIndexTemp )) {
165
+ Files .move (packageIndexTemp .toPath (), packageIndex .toPath (), StandardCopyOption .REPLACE_EXISTING );
166
+ } else {
167
+ log .info ("The cached files have been removed. {} {}" , packageIndexUrl , signatureUrl );
168
+ FileDownloader .invalidateFiles (packageIndexUrl , signatureUrl );
169
+ }
170
+ } else {
171
+ // Move the package index to the destination when the signature is not necessary
172
+ Files .move (packageIndexTemp .toPath (), packageIndex .toPath (), StandardCopyOption .REPLACE_EXISTING );
173
+ log .info ("The domain is not selected to verify the signature. will be copied into this path {}, packageIndex url: {}" , packageIndex , packageIndexUrl );
174
+ }
175
+ } catch (Exception e ) {
176
+ log .error ("Cannot download the package index from {} the package will be discard" , packageIndexUrl , e );
177
+ throw e ;
178
+ } finally {
179
+ // Delete useless temp file
180
+ Files .deleteIfExists (packageIndexTemp .toPath ());
181
+ }
182
+ }
183
+
184
+ public boolean verifyDomain (URL url ) {
185
+ final Collection <String > domain = PreferencesData .
186
+ getCollection ("http.signature_verify_domains" );
187
+ if (domain .size () == 0 ) {
188
+ // Default domain
189
+ domain .add ("downloads.arduino.cc" );
190
+ }
191
+ if (domain .contains (url .getHost ())) {
192
+ return true ;
193
+ } else {
194
+ log .info ("The domain is not selected to verify the signature. domain list: {}, url: {}" , domain , url );
195
+ return false ;
196
+ }
197
+ }
198
+
199
+ public boolean checkSignature (MultiStepProgress progress , URL signatureUrl , ProgressListener progressListener , SignatureVerifier signatureVerifier , String statusText , File fileToVerify ) throws Exception {
200
+
201
+ final boolean allowInsecurePackages =
202
+ PreferencesData .getBoolean ("allow_insecure_packages" , false );
203
+ if (allowInsecurePackages ) {
204
+ log .info ("Allow insecure packages is true the signature will be skip and return always verified" );
205
+ return true ;
206
+ }
207
+
208
+ // Signature file name
209
+ final String signatureFileName = FilenameUtils .getName (signatureUrl .getPath ());
210
+ final File packageIndexSignature = BaseNoGui .indexer .getIndexFile (signatureFileName );
211
+ final File packageIndexSignatureTemp = File .createTempFile (signatureFileName , ".tmp" );
212
+
213
+
214
+ try {
215
+ // Download signature
216
+ download (signatureUrl , packageIndexSignatureTemp , progress , statusText , progressListener , true );
217
+
218
+ // Verify the signature before move the files
219
+ final boolean signatureVerified = signatureVerifier .isSigned (fileToVerify , packageIndexSignatureTemp );
220
+ if (signatureVerified ) {
221
+ log .info ("Signature verified. url={}, signature url={}, file to verify={}, signature file={}" , signatureUrl , signatureUrl , fileToVerify , packageIndexSignatureTemp );
222
+ // Move if the signature is ok
223
+ Files .move (packageIndexSignatureTemp .toPath (), packageIndexSignature .toPath (), StandardCopyOption .REPLACE_EXISTING );
224
+ } else {
225
+ log .error ("{} file signature verification failed. File ignored." , signatureUrl );
226
+ System .err .println (format (tr ("{0} file signature verification failed. File ignored." ), signatureUrl .toString ()));
227
+ }
228
+ return signatureVerified ;
229
+ } catch (Exception e ) {
230
+ log .error ("Cannot download the signature from {} the package will be discard" , signatureUrl , e );
231
+ throw e ;
232
+ } finally {
233
+ Files .deleteIfExists (packageIndexSignatureTemp .toPath ());
234
+ }
235
+
236
+ }
237
+
143
238
}
0 commit comments