24
24
import re
25
25
import time
26
26
import argparse
27
+ import ssl
28
+ import contextlib
29
+
30
+ from urllib .request import urlretrieve
31
+ from urllib .request import urlopen
32
+ from urllib .response import addinfourl
33
+ from typing import IO , Any , Callable , Dict , Iterator , List , Optional , Set , Tuple , Union
34
+ from ssl import SSLContext
35
+
36
+ unicode = lambda s : str (s ) # noqa: E731
37
+
38
+ # the older "DigiCert Global Root CA" certificate used with github.com
39
+ DIGICERT_ROOT_CA_CERT = """
40
+ -----BEGIN CERTIFICATE-----
41
+ MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
42
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
43
+ d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
44
+ QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
45
+ MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
46
+ b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
47
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
48
+ CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
49
+ nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
50
+ 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
51
+ T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
52
+ gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
53
+ BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
54
+ TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
55
+ DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
56
+ hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
57
+ 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
58
+ PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
59
+ YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
60
+ CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
61
+ -----END CERTIFICATE-----
62
+ """
27
63
28
- # Initialize start_time globally
29
- start_time = - 1
30
-
31
- if sys .version_info [0 ] == 3 :
32
- from urllib .request import urlretrieve
33
- from urllib .request import urlopen
64
+ # the newer "DigiCert Global Root G2" certificate used with dl.espressif.com
65
+ DIGICERT_ROOT_G2_CERT = """
66
+ -----BEGIN CERTIFICATE-----
67
+ MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
68
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
69
+ d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
70
+ MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
71
+ MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
72
+ b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
73
+ 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
74
+ 2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
75
+ 1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
76
+ q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
77
+ tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
78
+ vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
79
+ BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
80
+ 5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
81
+ 1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
82
+ NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
83
+ Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
84
+ 8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
85
+ pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
86
+ MrY=
87
+ -----END CERTIFICATE-----
88
+ """
34
89
35
- unicode = lambda s : str (s ) # noqa: E731
36
- else :
37
- # Not Python 3 - today, it is most likely to be Python 2
38
- from urllib import urlretrieve
39
- from urllib import urlopen
90
+ DL_CERT_DICT = {'dl.espressif.com' : DIGICERT_ROOT_G2_CERT ,
91
+ 'github.com' : DIGICERT_ROOT_CA_CERT }
40
92
41
- if "Windows" in platform . system ():
42
- import requests
93
+ # Initialize start_time globally
94
+ start_time = - 1
43
95
44
96
# determine if application is a script file or frozen exe
45
97
if getattr (sys , "frozen" , False ):
@@ -71,20 +123,18 @@ def format_time(seconds):
71
123
return "{:02}:{:05.2f}" .format (int (minutes ), seconds )
72
124
73
125
74
- def report_progress (block_count , block_size , total_size , start_time ):
126
+ def report_progress (block_count , block_size , total_size ):
75
127
downloaded_size = block_count * block_size
76
- time_elapsed = time .time () - start_time
77
- current_speed = downloaded_size / (time_elapsed )
78
128
79
129
if sys .stdout .isatty ():
80
130
if total_size > 0 :
81
131
percent_complete = min ((downloaded_size / total_size ) * 100 , 100 )
82
132
sys .stdout .write (
83
- f"\r Downloading... { percent_complete :.2f} % - { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { format_time ( time_elapsed ) } - Speed: { current_speed / 1024 / 1024 :.2f } MB/s " # noqa: E501
133
+ f"\r Downloading... { percent_complete :.2f} % - { downloaded_size / 1024 / 1024 :.2f} MB downloaded" # noqa: E501
84
134
)
85
135
else :
86
136
sys .stdout .write (
87
- f"\r Downloading... { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { format_time ( time_elapsed ) } - Speed: { current_speed / 1024 / 1024 :.2f } MB/s " # noqa: E501
137
+ f"\r Downloading... { downloaded_size / 1024 / 1024 :.2f} MB downloaded" # noqa: E501
88
138
)
89
139
sys .stdout .flush ()
90
140
@@ -243,18 +293,25 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901
243
293
if filename .endswith ("tar.gz" ):
244
294
if not cfile :
245
295
cfile = tarfile .open (filename , "r:gz" )
246
- cfile .extractall (destination , filter = "tar " )
296
+ cfile .extractall (destination , filter = "fully_trusted " )
247
297
elif filename .endswith ("tar.xz" ):
248
298
if not cfile :
249
299
cfile = tarfile .open (filename , "r:xz" )
250
- cfile .extractall (destination , filter = "tar " )
300
+ cfile .extractall (destination , filter = "fully_trusted " )
251
301
elif filename .endswith ("zip" ):
252
302
if not cfile :
253
303
cfile = zipfile .ZipFile (filename )
254
304
cfile .extractall (destination )
255
305
else :
256
306
raise NotImplementedError ("Unsupported archive type" )
257
307
308
+ if sys .platform != 'win32' and filename .endswith ('zip' ) and isinstance (cfile , ZipFile ):
309
+ for file_info in cfile .infolist ():
310
+ extracted_file = os .path .join (destination , file_info .filename )
311
+ extracted_permissions = file_info .external_attr >> 16 & 0o777 # Extract Unix permissions
312
+ if os .path .exists (extracted_file ):
313
+ os .chmod (extracted_file , extracted_permissions )
314
+
258
315
if rename_to != dirname :
259
316
print ("Renaming {0} to {1} ..." .format (dirname , rename_to ))
260
317
shutil .move (dirname , rename_to )
@@ -275,55 +332,67 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901
275
332
return False
276
333
277
334
278
- def download_file_with_progress (url , filename , start_time ):
279
- import ssl
280
- import contextlib
281
-
282
- ctx = ssl .create_default_context ()
283
- ctx .check_hostname = False
284
- ctx .verify_mode = ssl .CERT_NONE
285
- with contextlib .closing (urlopen (url , context = ctx )) as fp :
286
- total_size = int (fp .getheader ("Content-Length" , fp .getheader ("Content-length" , "0" )))
287
- block_count = 0
288
- block_size = 1024 * 8
289
- block = fp .read (block_size )
290
- if block :
291
- with open (filename , "wb" ) as out_file :
292
- out_file .write (block )
293
- block_count += 1
294
- report_progress (block_count , block_size , total_size , start_time )
295
- while True :
296
- block = fp .read (block_size )
297
- if not block :
298
- break
299
- out_file .write (block )
300
- block_count += 1
301
- report_progress (block_count , block_size , total_size , start_time )
302
- else :
303
- raise Exception ("Non-existing file or connection error" )
304
-
305
-
306
- def download_file (url , filename ):
307
- import ssl
308
- import contextlib
309
-
310
- ctx = ssl .create_default_context ()
311
- ctx .check_hostname = False
312
- ctx .verify_mode = ssl .CERT_NONE
313
- with contextlib .closing (urlopen (url , context = ctx )) as fp :
314
- block_size = 1024 * 8
315
- block = fp .read (block_size )
316
- if block :
317
- with open (filename , "wb" ) as out_file :
318
- out_file .write (block )
319
- while True :
320
- block = fp .read (block_size )
321
- if not block :
322
- break
323
- out_file .write (block )
324
- else :
325
- raise Exception ("Non-existing file or connection error" )
326
-
335
+ def splittype (url : str ) -> Tuple [Optional [str ], str ]:
336
+ """
337
+ Splits given url into its type (e.g. https, file) and the rest.
338
+ """
339
+ match = re .match ('([^/:]+):(.*)' , url , re .DOTALL )
340
+ if match :
341
+ scheme , data = match .groups ()
342
+ return scheme .lower (), data
343
+ return None , url
344
+
345
+ def urlretrieve_ctx (url : str ,
346
+ filename : str ,
347
+ reporthook : Optional [Callable [[int , int , int ], None ]]= None ,
348
+ data : Optional [bytes ]= None ,
349
+ context : Optional [SSLContext ]= None ) -> Tuple [str , addinfourl ]:
350
+ """
351
+ Retrieve data from given URL. An alternative version of urlretrieve which takes SSL context as an argument.
352
+ """
353
+ url_type , path = splittype (url )
354
+
355
+ # urlopen doesn't have context argument in Python <=2.7.9
356
+ extra_urlopen_args = {}
357
+ if context :
358
+ extra_urlopen_args ['context' ] = context
359
+ with contextlib .closing (urlopen (url , data , ** extra_urlopen_args )) as fp : # type: ignore
360
+ headers = fp .info ()
361
+
362
+ # Just return the local path and the "headers" for file://
363
+ # URLs. No sense in performing a copy unless requested.
364
+ if url_type == 'file' and not filename :
365
+ return os .path .normpath (path ), headers
366
+
367
+ # Handle temporary file setup.
368
+ tfp = open (filename , 'wb' )
369
+
370
+ with tfp :
371
+ result = filename , headers
372
+ bs = 1024 * 8
373
+ size = int (headers .get ('content-length' , - 1 ))
374
+ read = 0
375
+ blocknum = 0
376
+
377
+ if reporthook :
378
+ reporthook (blocknum , bs , size )
379
+
380
+ while True :
381
+ block = fp .read (bs )
382
+ if not block :
383
+ break
384
+ read += len (block )
385
+ tfp .write (block )
386
+ blocknum += 1
387
+ if reporthook :
388
+ reporthook (blocknum , bs , size )
389
+
390
+ if size >= 0 and read < size :
391
+ raise ContentTooShortError (
392
+ 'retrieval incomplete: got only %i out of %i bytes'
393
+ % (read , size ), result )
394
+
395
+ return result
327
396
328
397
def get_tool (tool , force_download , force_extract ):
329
398
sys_name = platform .system ()
@@ -339,29 +408,22 @@ def get_tool(tool, force_download, force_extract):
339
408
else :
340
409
print ("Downloading '" + archive_name + "' ..." )
341
410
sys .stdout .flush ()
342
- if "CYGWIN_NT" in sys_name :
343
- import ssl
344
-
345
- ctx = ssl .create_default_context ()
346
- ctx .check_hostname = False
347
- ctx .verify_mode = ssl .CERT_NONE
348
- urlretrieve (url , local_path , report_progress , context = ctx )
349
- elif "Windows" in sys_name :
350
- r = requests .get (url )
351
- f = open (local_path , "wb" )
352
- f .write (r .content )
353
- f .close ()
354
- else :
355
- is_ci = os .environ .get ("GITHUB_WORKSPACE" )
356
- if is_ci :
357
- download_file (url , local_path )
411
+
412
+ try :
413
+ for site , cert in DL_CERT_DICT .items ():
414
+ if site in url :
415
+ ctx = ssl .create_default_context ()
416
+ ctx .load_verify_locations (cadata = cert )
417
+ break
358
418
else :
359
- try :
360
- urlretrieve (url , local_path , report_progress )
361
- except : # noqa: E722
362
- download_file_with_progress (url , local_path , start_time )
363
- sys .stdout .write (" - Done\n " )
364
- sys .stdout .flush ()
419
+ ctx = None
420
+
421
+ urlretrieve_ctx (url , local_path , report_progress , context = ctx )
422
+ except Exception as e :
423
+ print (f"Failed to download { archive_name } : { e } " )
424
+ return False
425
+ finally :
426
+ sys .stdout .flush ()
365
427
else :
366
428
print ("Tool {0} already downloaded" .format (archive_name ))
367
429
sys .stdout .flush ()
0 commit comments