Skip to content

Commit d21f661

Browse files
committed
Created IndexResource and factored out all download utilities
1 parent fc80bf0 commit d21f661

File tree

2 files changed

+128
-97
lines changed

2 files changed

+128
-97
lines changed

arduino/resources/index.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to license@arduino.cc.
15+
16+
package resources
17+
18+
import (
19+
"net/url"
20+
"path"
21+
"strings"
22+
23+
"github.com/arduino/arduino-cli/arduino"
24+
"github.com/arduino/arduino-cli/arduino/security"
25+
"github.com/arduino/go-paths-helper"
26+
"go.bug.st/downloader/v2"
27+
)
28+
29+
// IndexResource is a reference to a package_index.json
30+
type IndexResource struct {
31+
URL *url.URL
32+
SignatureURL *url.URL
33+
}
34+
35+
// Download will download the index and possibly check the signature using the Arduino's public key.
36+
// If the file is in .gz format it will be unpacked first.
37+
func (res *IndexResource) Download(destDir *paths.Path, downloadCB DownloadProgressCB) error {
38+
// Create destination directory
39+
if err := destDir.MkdirAll(); err != nil {
40+
return &arduino.PermissionDeniedError{Message: tr("Can't create data directory %s", destDir), Cause: err}
41+
}
42+
43+
// Create a temp dir to stage all downloads
44+
tmp, err := paths.MkTempDir("", "library_index_download")
45+
if err != nil {
46+
return &arduino.TempDirCreationFailedError{Cause: err}
47+
}
48+
defer tmp.RemoveAll()
49+
50+
// Download index file
51+
indexFileName := path.Base(res.URL.Path) // == pakcage_index.json[.gz]
52+
tmpIndexPath := tmp.Join(indexFileName)
53+
if err := DownloadFile(tmpIndexPath, res.URL.String(), tr("Downloading index: %s", indexFileName), downloadCB, nil, downloader.NoResume); err != nil {
54+
return &arduino.FailedDownloadError{Message: tr("Error downloading index '%s'", res.URL), Cause: err}
55+
}
56+
57+
// Expand the index if it is compressed
58+
if strings.HasSuffix(indexFileName, ".gz") {
59+
indexFileName = strings.TrimSuffix(indexFileName, ".gz") // == pakcage_index.json
60+
tmpUnzippedIndexPath := tmp.Join(indexFileName)
61+
if err := paths.GUnzip(tmpIndexPath, tmpUnzippedIndexPath); err != nil {
62+
return &arduino.PermissionDeniedError{Message: tr("Error extracting %s", indexFileName), Cause: err}
63+
}
64+
tmpIndexPath = tmpUnzippedIndexPath
65+
}
66+
67+
// Check the signature if needed
68+
var signaturePath, tmpSignaturePath *paths.Path
69+
if res.SignatureURL != nil {
70+
// Compose signature URL
71+
signatureFileName := path.Base(res.SignatureURL.Path)
72+
73+
// Download signature
74+
signaturePath = destDir.Join(signatureFileName)
75+
tmpSignaturePath = tmp.Join(signatureFileName)
76+
if err := DownloadFile(tmpSignaturePath, res.SignatureURL.String(), tr("Downloading index signature: %s", signatureFileName), downloadCB, nil, downloader.NoResume); err != nil {
77+
return &arduino.FailedDownloadError{Message: tr("Error downloading index signature '%s'", res.SignatureURL), Cause: err}
78+
}
79+
80+
// Check signature...
81+
if valid, _, err := security.VerifyArduinoDetachedSignature(tmpIndexPath, tmpSignaturePath); err != nil {
82+
return &arduino.PermissionDeniedError{Message: tr("Error verifying signature"), Cause: err}
83+
} else if !valid {
84+
return &arduino.SignatureVerificationFailedError{File: res.URL.String()}
85+
}
86+
}
87+
88+
// TODO: Implement a ResourceValidator
89+
// if !validate(tmpIndexPath) { return error }
90+
91+
// Make a backup copy of old index and signature so the defer function can rollback in case of errors.
92+
indexPath := destDir.Join(indexFileName)
93+
oldIndex := tmp.Join("old_index")
94+
if err := indexPath.CopyTo(oldIndex); err != nil {
95+
return &arduino.PermissionDeniedError{Message: tr("Error saving downloaded index %s", res.URL), Cause: err}
96+
}
97+
defer oldIndex.CopyTo(indexPath) // will silently fail in case of success
98+
oldSignature := tmp.Join("old_signature")
99+
if err := signaturePath.CopyTo(oldSignature); err != nil {
100+
return &arduino.PermissionDeniedError{Message: tr("Error saving downloaded index %s", res.URL), Cause: err}
101+
}
102+
defer oldSignature.CopyTo(signaturePath) // will silently fail in case of success
103+
104+
if err := tmpIndexPath.CopyTo(indexPath); err != nil {
105+
return &arduino.PermissionDeniedError{Message: tr("Error saving downloaded index %s", res.URL), Cause: err}
106+
}
107+
if res.SignatureURL != nil {
108+
if err := tmpSignaturePath.CopyTo(signaturePath); err != nil {
109+
return &arduino.PermissionDeniedError{Message: tr("Error saving downloaded index signature"), Cause: err}
110+
}
111+
}
112+
oldIndex.Remove()
113+
oldSignature.Remove()
114+
return nil
115+
}

commands/instances.go

Lines changed: 13 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ import (
1919
"context"
2020
"errors"
2121
"fmt"
22-
"io/ioutil"
2322
"net/url"
2423
"os"
25-
"path"
24+
"strings"
2625

2726
"github.com/arduino/arduino-cli/arduino"
2827
"github.com/arduino/arduino-cli/arduino/cores"
@@ -32,7 +31,6 @@ import (
3231
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
3332
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
3433
"github.com/arduino/arduino-cli/arduino/resources"
35-
"github.com/arduino/arduino-cli/arduino/security"
3634
sk "github.com/arduino/arduino-cli/arduino/sketch"
3735
"github.com/arduino/arduino-cli/arduino/utils"
3836
"github.com/arduino/arduino-cli/cli/globals"
@@ -41,7 +39,6 @@ import (
4139
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
4240
paths "github.com/arduino/go-paths-helper"
4341
"github.com/sirupsen/logrus"
44-
"go.bug.st/downloader/v2"
4542
"google.golang.org/grpc/codes"
4643
"google.golang.org/grpc/status"
4744
)
@@ -381,41 +378,12 @@ func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequ
381378
}
382379
defer tmp.RemoveAll()
383380

384-
// Download gzipped library_index
385-
tmpIndexGz := tmp.Join("library_index.json.gz")
386-
tmpIndexURL := librariesmanager.LibraryIndexGZURL.String()
387-
if err := resources.DownloadFile(tmpIndexGz, tmpIndexURL, tr("Updating index: library_index.json.gz"), downloadCB.FromRPC(), nil, downloader.NoResume); err != nil {
388-
return &arduino.FailedDownloadError{Message: tr("Error downloading library_index.json.gz"), Cause: err}
381+
indexResource := resources.IndexResource{
382+
URL: librariesmanager.LibraryIndexGZURL,
383+
SignatureURL: librariesmanager.LibraryIndexSignature,
389384
}
390-
391-
// Download signature
392-
tmpSignature := tmp.Join("library_index.json.sig")
393-
tmpSignatureURL := librariesmanager.LibraryIndexSignature.String()
394-
if err := resources.DownloadFile(tmpSignature, tmpSignatureURL, tr("Updating index: library_index.json.sig"), downloadCB.FromRPC(), nil, downloader.NoResume); err != nil {
395-
return &arduino.FailedDownloadError{Message: tr("Error downloading library_index.json.sig"), Cause: err}
396-
}
397-
398-
// Extract the real library_index
399-
tmpIndex := tmp.Join("library_index.json")
400-
if err := paths.GUnzip(tmpIndexGz, tmpIndex); err != nil {
401-
return &arduino.PermissionDeniedError{Message: tr("Error extracting library_index.json.gz"), Cause: err}
402-
}
403-
404-
// Check signature
405-
if ok, _, err := security.VerifyArduinoDetachedSignature(tmpIndex, tmpSignature); err != nil {
406-
return &arduino.PermissionDeniedError{Message: tr("Error verifying signature"), Cause: err}
407-
} else if !ok {
408-
return &arduino.SignatureVerificationFailedError{File: "library_index.json"}
409-
}
410-
411-
// Copy extracted library_index and signature to final destination
412-
lm.IndexFile.Remove()
413-
lm.IndexFileSignature.Remove()
414-
if err := tmpIndex.CopyTo(lm.IndexFile); err != nil {
415-
return &arduino.PermissionDeniedError{Message: tr("Error writing library_index.json"), Cause: err}
416-
}
417-
if err := tmpSignature.CopyTo(lm.IndexFileSignature); err != nil {
418-
return &arduino.PermissionDeniedError{Message: tr("Error writing library_index.json.sig"), Cause: err}
385+
if err := indexResource.Download(lm.IndexFile.Parent(), downloadCB.FromRPC()); err != nil {
386+
return err
419387
}
420388

421389
return nil
@@ -458,67 +426,15 @@ func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB Do
458426
continue
459427
}
460428

461-
var tmp *paths.Path
462-
if tmpFile, err := ioutil.TempFile("", ""); err != nil {
463-
return nil, &arduino.TempFileCreationFailedError{Cause: err}
464-
} else if err := tmpFile.Close(); err != nil {
465-
return nil, &arduino.TempFileCreationFailedError{Cause: err}
466-
} else {
467-
tmp = paths.New(tmpFile.Name())
468-
}
469-
defer tmp.Remove()
470-
471-
coreIndexPath := indexpath.Join(path.Base(URL.Path))
472-
if err := resources.DownloadFile(tmp, URL.String(), tr("Updating index: %s", coreIndexPath.Base()), downloadCB.FromRPC(), nil, downloader.NoResume); err != nil {
473-
return nil, &arduino.FailedDownloadError{Message: tr("Error downloading index '%s'", URL), Cause: err}
474-
}
475-
476-
// Check for signature
477-
var tmpSig *paths.Path
478-
var coreIndexSigPath *paths.Path
479-
if URL.Hostname() == "downloads.arduino.cc" {
480-
URLSig, err := url.Parse(URL.String())
481-
if err != nil {
482-
return nil, &arduino.InvalidURLError{Cause: err}
483-
}
484-
URLSig.Path += ".sig"
485-
486-
if t, err := ioutil.TempFile("", ""); err != nil {
487-
return nil, &arduino.TempFileCreationFailedError{Cause: err}
488-
} else if err := t.Close(); err != nil {
489-
return nil, &arduino.TempFileCreationFailedError{Cause: err}
490-
} else {
491-
tmpSig = paths.New(t.Name())
492-
}
493-
defer tmpSig.Remove()
494-
495-
coreIndexSigPath = indexpath.Join(path.Base(URLSig.Path))
496-
if err := resources.DownloadFile(tmpSig, URLSig.String(), tr("Updating index: %s", coreIndexSigPath.Base()), downloadCB.FromRPC(), nil, downloader.NoResume); err != nil {
497-
return nil, &arduino.FailedDownloadError{Message: tr("Error downloading index signature '%s'", URLSig), Cause: err}
498-
}
499-
500-
if valid, _, err := security.VerifyArduinoDetachedSignature(tmp, tmpSig); err != nil {
501-
return nil, &arduino.PermissionDeniedError{Message: tr("Error verifying signature"), Cause: err}
502-
} else if !valid {
503-
return nil, &arduino.SignatureVerificationFailedError{File: URL.String()}
504-
}
429+
indexResource := resources.IndexResource{
430+
URL: URL,
505431
}
506-
507-
if _, err := packageindex.LoadIndex(tmp); err != nil {
508-
return nil, &arduino.InvalidArgumentError{Message: tr("Invalid package index in %s", URL), Cause: err}
509-
}
510-
511-
if err := indexpath.MkdirAll(); err != nil {
512-
return nil, &arduino.PermissionDeniedError{Message: tr("Can't create data directory %s", indexpath), Cause: err}
432+
if strings.HasSuffix(URL.Host, "arduino.cc") {
433+
indexResource.SignatureURL, _ = url.Parse(u) // should not fail because we already parsed it
434+
indexResource.SignatureURL.Path += ".sig"
513435
}
514-
515-
if err := tmp.CopyTo(coreIndexPath); err != nil {
516-
return nil, &arduino.PermissionDeniedError{Message: tr("Error saving downloaded index %s", URL), Cause: err}
517-
}
518-
if tmpSig != nil {
519-
if err := tmpSig.CopyTo(coreIndexSigPath); err != nil {
520-
return nil, &arduino.PermissionDeniedError{Message: tr("Error saving downloaded index signature"), Cause: err}
521-
}
436+
if err := indexResource.Download(indexpath, downloadCB.FromRPC()); err != nil {
437+
return nil, err
522438
}
523439
}
524440

0 commit comments

Comments
 (0)