@@ -2,8 +2,10 @@ package tools
2
2
3
3
import (
4
4
"archive/tar"
5
+ "archive/zip"
5
6
"bytes"
6
7
"compress/bzip2"
8
+ "compress/gzip"
7
9
"crypto/sha256"
8
10
"encoding/hex"
9
11
"encoding/json"
@@ -13,10 +15,13 @@ import (
13
15
"net/http"
14
16
"os"
15
17
"path"
18
+ "path/filepath"
16
19
"runtime"
20
+ "strings"
17
21
18
22
"github.com/arduino/arduino-create-agent/utilities"
19
- "github.com/pivotal-golang/archiver/extractor"
23
+ "github.com/blang/semver"
24
+ "github.com/xrash/smetrics"
20
25
)
21
26
22
27
type system struct {
@@ -48,6 +53,10 @@ var systems = map[string]string{
48
53
"windows386" : "i686-mingw32" ,
49
54
}
50
55
56
+ func mimeType (data []byte ) (string , error ) {
57
+ return http .DetectContentType (data [0 :512 ]), nil
58
+ }
59
+
51
60
// Download will parse the index at the indexURL for the tool to download.
52
61
// It will extract it in a folder in .arduino-create, and it will update the
53
62
// Installed map.
@@ -92,6 +101,8 @@ func (t *Tools) Download(name, version, behaviour string) error {
92
101
var data index
93
102
json .Unmarshal (body , & data )
94
103
104
+ t .Logger .Println (string (body ))
105
+
95
106
// Find the tool by name
96
107
correctTool := findTool (name , version , data )
97
108
@@ -101,10 +112,13 @@ func (t *Tools) Download(name, version, behaviour string) error {
101
112
102
113
// Find the url based on system
103
114
var correctSystem system
115
+ max_similarity := 0.8
104
116
105
117
for _ , s := range correctTool .Systems {
106
- if s .Host == systems [runtime .GOOS + runtime .GOARCH ] {
118
+ similarity := smetrics .Jaro (s .Host , systems [runtime .GOOS + runtime .GOARCH ])
119
+ if similarity > max_similarity {
107
120
correctSystem = s
121
+ max_similarity = similarity
108
122
}
109
123
}
110
124
@@ -133,18 +147,26 @@ func (t *Tools) Download(name, version, behaviour string) error {
133
147
// Decompress
134
148
t .Logger .Println ("Unpacking tool " + name )
135
149
136
- location := path .Join (dir (), name , version )
150
+ location := path .Join (dir (), correctTool . Name , correctTool . Version )
137
151
err = os .RemoveAll (location )
138
152
139
153
if err != nil {
140
154
return err
141
155
}
142
156
143
- switch path .Ext (correctSystem .URL ) {
144
- case ".zip" :
157
+ srcType , err := mimeType (body )
158
+ if err != nil {
159
+ return err
160
+ }
161
+
162
+ switch srcType {
163
+ case "application/zip" :
145
164
location , err = extractZip (body , location )
146
- case ".bz2" :
165
+ case "application/x-bz2" :
166
+ case "application/octet-stream" :
147
167
location , err = extractBz2 (body , location )
168
+ case "application/x-gzip" :
169
+ location , err = extractTarGz (body , location )
148
170
default :
149
171
return errors .New ("Unknown extension for file " + correctSystem .URL )
150
172
}
@@ -155,20 +177,18 @@ func (t *Tools) Download(name, version, behaviour string) error {
155
177
156
178
// Ensure that the files are executable
157
179
t .Logger .Println ("Ensure that the files are executable" )
158
- err = makeExecutable (location )
159
- if err != nil {
160
- return err
161
- }
162
180
163
181
// Update the tool map
164
182
t .Logger .Println ("Updating map with location " + location )
165
183
166
- t .installed [key ] = location
184
+ t .installed [name ] = location
185
+ t .installed [name + "-" + correctTool .Version ] = location
167
186
return t .writeMap ()
168
187
}
169
188
170
189
func findTool (name , version string , data index ) tool {
171
190
var correctTool tool
191
+ correctTool .Version = "0.0"
172
192
173
193
for _ , p := range data .Packages {
174
194
for _ , t := range p .Tools {
@@ -178,7 +198,9 @@ func findTool(name, version string, data index) tool {
178
198
}
179
199
} else {
180
200
// Find latest
181
- if t .Name == name && t .Version > correctTool .Version {
201
+ v1 , _ := semver .Make (t .Version )
202
+ v2 , _ := semver .Make (correctTool .Version )
203
+ if t .Name == name && v1 .Compare (v2 ) > 0 {
182
204
correctTool = t
183
205
}
184
206
}
@@ -187,75 +209,185 @@ func findTool(name, version string, data index) tool {
187
209
return correctTool
188
210
}
189
211
212
+ func findBaseDir (dirList []string ) string {
213
+ baseDir := ""
214
+ for index , _ := range dirList {
215
+ candidateBaseDir := dirList [index ]
216
+ for i := index ; i < len (dirList ); i ++ {
217
+ if ! strings .Contains (dirList [i ], candidateBaseDir ) {
218
+ return baseDir
219
+ }
220
+ }
221
+ baseDir = candidateBaseDir
222
+ }
223
+ return baseDir
224
+ }
225
+
190
226
func extractZip (body []byte , location string ) (string , error ) {
191
227
path , err := utilities .SaveFileonTempDir ("tooldownloaded.zip" , bytes .NewReader (body ))
228
+ r , err := zip .OpenReader (path )
192
229
if err != nil {
193
- return "" , err
230
+ return location , err
194
231
}
195
232
196
- e := extractor .NewZip ()
197
- err = e .Extract (path , location )
198
- if err != nil {
199
- return "" , err
233
+ var dirList []string
234
+
235
+ for _ , f := range r .File {
236
+ if f .FileInfo ().IsDir () {
237
+ dirList = append (dirList , f .Name )
238
+ }
239
+ }
240
+
241
+ basedir := findBaseDir (dirList )
242
+
243
+ for _ , f := range r .File {
244
+ fullname := filepath .Join (location , strings .Replace (f .Name , basedir , "" , - 1 ))
245
+ if f .FileInfo ().IsDir () {
246
+ os .MkdirAll (fullname , f .FileInfo ().Mode ().Perm ())
247
+ } else {
248
+ os .MkdirAll (filepath .Dir (fullname ), 0755 )
249
+ perms := f .FileInfo ().Mode ().Perm ()
250
+ out , err := os .OpenFile (fullname , os .O_CREATE | os .O_RDWR , perms )
251
+ if err != nil {
252
+ return location , err
253
+ }
254
+ rc , err := f .Open ()
255
+ if err != nil {
256
+ return location , err
257
+ }
258
+ _ , err = io .CopyN (out , rc , f .FileInfo ().Size ())
259
+ if err != nil {
260
+ return location , err
261
+ }
262
+ rc .Close ()
263
+ out .Close ()
264
+
265
+ mtime := f .FileInfo ().ModTime ()
266
+ err = os .Chtimes (fullname , mtime , mtime )
267
+ if err != nil {
268
+ return location , err
269
+ }
270
+ }
200
271
}
201
- return "" , nil
272
+ return location , nil
202
273
}
203
274
204
- func extractBz2 (body []byte , location string ) (string , error ) {
205
- tarFile := bzip2 .NewReader (bytes .NewReader (body ))
275
+ func extractTarGz (body []byte , location string ) (string , error ) {
276
+ bodyCopy := make ([]byte , len (body ))
277
+ copy (bodyCopy , body )
278
+ tarFile , _ := gzip .NewReader (bytes .NewReader (body ))
206
279
tarReader := tar .NewReader (tarFile )
207
280
208
- var subfolder string
281
+ var dirList [] string
209
282
210
- i := 0
211
283
for {
212
284
header , err := tarReader .Next ()
285
+ if err == io .EOF {
286
+ break
287
+ }
288
+ if header .FileInfo ().IsDir () {
289
+ dirList = append (dirList , header .Name )
290
+ }
291
+ }
292
+
293
+ basedir := findBaseDir (dirList )
294
+
295
+ tarFile , _ = gzip .NewReader (bytes .NewReader (bodyCopy ))
296
+ tarReader = tar .NewReader (tarFile )
213
297
298
+ for {
299
+ header , err := tarReader .Next ()
214
300
if err == io .EOF {
215
301
break
302
+ } else if err != nil {
303
+ //return location, err
304
+ }
305
+
306
+ path := filepath .Join (location , strings .Replace (header .Name , basedir , "" , - 1 ))
307
+ info := header .FileInfo ()
308
+
309
+ if info .IsDir () {
310
+ if err = os .MkdirAll (path , info .Mode ()); err != nil {
311
+ return location , err
312
+ }
313
+ continue
314
+ }
315
+
316
+ if header .Typeflag == tar .TypeSymlink {
317
+ err = os .Symlink (header .Linkname , path )
318
+ continue
216
319
}
217
320
321
+ file , err := os .OpenFile (path , os .O_CREATE | os .O_TRUNC | os .O_WRONLY , info .Mode ())
322
+ if err != nil {
323
+ //return location, err
324
+ }
325
+ defer file .Close ()
326
+ _ , err = io .Copy (file , tarReader )
218
327
if err != nil {
219
- return "" , err
328
+ // return location , err
220
329
}
330
+ }
331
+ return location , nil
332
+ }
333
+
334
+ func extractBz2 (body []byte , location string ) (string , error ) {
335
+ bodyCopy := make ([]byte , len (body ))
336
+ copy (bodyCopy , body )
337
+ tarFile := bzip2 .NewReader (bytes .NewReader (body ))
338
+ tarReader := tar .NewReader (tarFile )
221
339
222
- filePath := path . Join ( location , header . Name )
340
+ var dirList [] string
223
341
224
- // We get the name of the subfolder
225
- if i == 0 {
226
- subfolder = filePath
342
+ for {
343
+ header , err := tarReader .Next ()
344
+ if err == io .EOF {
345
+ break
227
346
}
347
+ if header .FileInfo ().IsDir () {
348
+ dirList = append (dirList , header .Name )
349
+ }
350
+ }
228
351
229
- switch header .Typeflag {
230
- case tar .TypeDir :
231
- err = os .MkdirAll (filePath , os .FileMode (header .Mode ))
232
- case tar .TypeReg :
233
- f , err := os .Create (filePath )
234
- if err != nil {
235
- break
236
- }
237
- defer f .Close ()
238
- _ , err = io .Copy (f , tarReader )
239
- case tar .TypeRegA :
240
- f , err := os .Create (filePath )
241
- if err != nil {
242
- break
352
+ basedir := findBaseDir (dirList )
353
+
354
+ tarFile = bzip2 .NewReader (bytes .NewReader (bodyCopy ))
355
+ tarReader = tar .NewReader (tarFile )
356
+
357
+ for {
358
+ header , err := tarReader .Next ()
359
+ if err == io .EOF {
360
+ break
361
+ } else if err != nil {
362
+ //return location, err
363
+ }
364
+
365
+ path := filepath .Join (location , strings .Replace (header .Name , basedir , "" , - 1 ))
366
+ info := header .FileInfo ()
367
+
368
+ if info .IsDir () {
369
+ if err = os .MkdirAll (path , info .Mode ()); err != nil {
370
+ return location , err
243
371
}
244
- defer f .Close ()
245
- _ , err = io .Copy (f , tarReader )
246
- case tar .TypeSymlink :
247
- err = os .Symlink (header .Linkname , filePath )
248
- default :
249
- err = errors .New ("Unknown header in tar.bz2 file" )
372
+ continue
250
373
}
251
374
252
- if err != nil {
253
- return "" , err
375
+ if header .Typeflag == tar .TypeSymlink {
376
+ err = os .Symlink (header .Linkname , path )
377
+ continue
254
378
}
255
379
256
- i ++
380
+ file , err := os .OpenFile (path , os .O_CREATE | os .O_TRUNC | os .O_WRONLY , info .Mode ())
381
+ if err != nil {
382
+ //return location, err
383
+ }
384
+ defer file .Close ()
385
+ _ , err = io .Copy (file , tarReader )
386
+ if err != nil {
387
+ //return location, err
388
+ }
257
389
}
258
- return subfolder , nil
390
+ return location , nil
259
391
}
260
392
261
393
func makeExecutable (location string ) error {
0 commit comments