]> jfr.im git - yt-dlp.git/blame - youtube_dlc/update.py
flake8. added sha256 check to updater.
[yt-dlp.git] / youtube_dlc / update.py
CommitLineData
15938ab6
PH
1from __future__ import unicode_literals
2
d2790370 3import io
d5ed35b6
FV
4import json
5import traceback
6import hashlib
ce02ed60 7import os
d2790370 8import subprocess
46353f67 9import sys
d5ed35b6
FV
10from zipimport import zipimporter
11
bfe2b8cf 12from .compat import compat_realpath
c0384f22 13from .utils import encode_compat_str
d3d3e2e3 14
d5ed35b6
FV
15from .version import __version__
16
5f6a1245 17
d5ed35b6 18def rsa_verify(message, signature, key):
d5ed35b6 19 from hashlib import sha256
15938ab6 20 assert isinstance(message, bytes)
4d318be1
FV
21 byte_size = (len(bin(key[0])) - 2 + 8 - 1) // 8
22 signature = ('%x' % pow(int(signature, 16), key[1], key[0])).encode()
23 signature = (byte_size * 2 - len(signature)) * b'0' + signature
24 asn1 = b'3031300d060960864801650304020105000420'
25 asn1 += sha256(message).hexdigest().encode()
26 if byte_size < len(asn1) // 2 + 11:
5f6a1245 27 return False
4d318be1
FV
28 expected = b'0001' + (byte_size - len(asn1) // 2 - 3) * b'ff' + b'00' + asn1
29 return expected == signature
d5ed35b6 30
d7386f62 31
0c3e5f49 32def update_self(to_screen, verbose, opener):
d5ed35b6
FV
33 """Update the program file with the latest version from the repository"""
34
915f2a92 35 UPDATE_URL = 'https://blackjack4494.github.io//update/'
d5ed35b6
FV
36 VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
37 JSON_URL = UPDATE_URL + 'versions.json'
38 UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
39
fa57af1e
U
40 def sha256sum():
41 h = hashlib.sha256()
42 b = bytearray(128 * 1024)
43 mv = memoryview(b)
44 with open(os.path.realpath(sys.executable), 'rb', buffering=0) as f:
45 for n in iter(lambda: f.readinto(mv), 0):
46 h.update(mv[:n])
47 return h.hexdigest()
48
49 to_screen('Current Build Hash %s' % sha256sum())
50
611c1dd9 51 if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, 'frozen'):
cefecac1 52 to_screen('It looks like you installed youtube-dlc with a package manager, pip, setup.py or a tarball. Please use that to update.')
d5ed35b6
FV
53 return
54
fa57af1e
U
55 # compiled file.exe can find itself by
56 # to_screen(os.path.basename(sys.executable))
57 # and path to py or exe
58 # to_screen(os.path.realpath(sys.executable))
59
d5ed35b6
FV
60 # Check if there is a new version
61 try:
0c3e5f49 62 newversion = opener.open(VERSION_URL).read().decode('utf-8').strip()
70a1165b 63 except Exception:
5f6a1245 64 if verbose:
c0384f22 65 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 66 to_screen('ERROR: can\'t find the current version. Please try again later.')
fa57af1e 67 to_screen('Visit https://github.com/blackjack4494/yt-dlc/releases/latest')
d5ed35b6
FV
68 return
69 if newversion == __version__:
cefecac1 70 to_screen('youtube-dlc is up-to-date (' + __version__ + ')')
d5ed35b6
FV
71 return
72
73 # Download and check versions info
74 try:
0c3e5f49 75 versions_info = opener.open(JSON_URL).read().decode('utf-8')
d5ed35b6 76 versions_info = json.loads(versions_info)
70a1165b 77 except Exception:
5f6a1245 78 if verbose:
c0384f22 79 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 80 to_screen('ERROR: can\'t obtain versions info. Please try again later.')
fa57af1e 81 to_screen('Visit https://github.com/blackjack4494/yt-dlc/releases/latest')
d5ed35b6 82 return
83e865a3 83 if 'signature' not in versions_info:
15938ab6 84 to_screen('ERROR: the versions file is not signed or corrupted. Aborting.')
d5ed35b6
FV
85 return
86 signature = versions_info['signature']
87 del versions_info['signature']
88 if not rsa_verify(json.dumps(versions_info, sort_keys=True).encode('utf-8'), signature, UPDATES_RSA_KEY):
15938ab6 89 to_screen('ERROR: the versions file signature is invalid. Aborting.')
d5ed35b6
FV
90 return
91
d2790370 92 version_id = versions_info['latest']
d7386f62
PH
93
94 def version_tuple(version_str):
95 return tuple(map(int, version_str.split('.')))
2e767313 96 if version_tuple(__version__) >= version_tuple(version_id):
cefecac1 97 to_screen('youtube-dlc is up to date (%s)' % __version__)
d7386f62
PH
98 return
99
15938ab6 100 to_screen('Updating to version ' + version_id + ' ...')
d2790370 101 version = versions_info['versions'][version_id]
3bf79c75 102
46a127ee 103 print_notes(to_screen, versions_info['versions'])
d5ed35b6 104
e9297256 105 # sys.executable is set to the full pathname of the exe-file for py2exe
bfe2b8cf
S
106 # though symlinks are not followed so that we need to do this manually
107 # with help of realpath
108 filename = compat_realpath(sys.executable if hasattr(sys, 'frozen') else sys.argv[0])
46353f67 109
d5ed35b6 110 if not os.access(filename, os.W_OK):
15938ab6 111 to_screen('ERROR: no write permissions on %s' % filename)
d5ed35b6
FV
112 return
113
114 # Py2EXE
611c1dd9 115 if hasattr(sys, 'frozen'):
e9297256 116 exe = filename
d5ed35b6
FV
117 directory = os.path.dirname(exe)
118 if not os.access(directory, os.W_OK):
15938ab6 119 to_screen('ERROR: no write permissions on %s' % directory)
d5ed35b6
FV
120 return
121
122 try:
aa2fd598 123 urlh = opener.open(version['exe'][0])
d5ed35b6
FV
124 newcontent = urlh.read()
125 urlh.close()
0b63aed8 126 except (IOError, OSError):
5f6a1245 127 if verbose:
c0384f22 128 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 129 to_screen('ERROR: unable to download latest version')
fa57af1e 130 to_screen('Visit https://github.com/blackjack4494/yt-dlc/releases/latest')
d5ed35b6
FV
131 return
132
133 newcontent_hash = hashlib.sha256(newcontent).hexdigest()
134 if newcontent_hash != version['exe'][1]:
15938ab6 135 to_screen('ERROR: the downloaded file hash does not match. Aborting.')
d5ed35b6
FV
136 return
137
138 try:
139 with open(exe + '.new', 'wb') as outf:
140 outf.write(newcontent)
0b63aed8 141 except (IOError, OSError):
5f6a1245 142 if verbose:
c0384f22 143 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 144 to_screen('ERROR: unable to write the new version')
d5ed35b6
FV
145 return
146
147 try:
cefecac1 148 bat = os.path.join(directory, 'youtube-dlc-updater.bat')
d2790370 149 with io.open(bat, 'w') as batfile:
15938ab6 150 batfile.write('''
d2790370
PH
151@echo off
152echo Waiting for file handle to be closed ...
d5ed35b6 153ping 127.0.0.1 -n 5 -w 1000 > NUL
d2790370 154move /Y "%s.new" "%s" > NUL
cefecac1 155echo Updated youtube-dlc to version %s.
d2790370 156start /b "" cmd /c del "%%~f0"&exit /b"
15938ab6 157 \n''' % (exe, exe, version_id))
d5ed35b6 158
d2790370
PH
159 subprocess.Popen([bat]) # Continues to run in the background
160 return # Do not show premature success messages
0b63aed8 161 except (IOError, OSError):
5f6a1245 162 if verbose:
c0384f22 163 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 164 to_screen('ERROR: unable to overwrite current version')
d5ed35b6
FV
165 return
166
167 # Zip unix package
168 elif isinstance(globals().get('__loader__'), zipimporter):
169 try:
aa2fd598 170 urlh = opener.open(version['bin'][0])
d5ed35b6
FV
171 newcontent = urlh.read()
172 urlh.close()
0b63aed8 173 except (IOError, OSError):
5f6a1245 174 if verbose:
c0384f22 175 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 176 to_screen('ERROR: unable to download latest version')
fa57af1e 177 to_screen('Visit https://github.com/blackjack4494/yt-dlc/releases/latest')
d5ed35b6
FV
178 return
179
180 newcontent_hash = hashlib.sha256(newcontent).hexdigest()
181 if newcontent_hash != version['bin'][1]:
15938ab6 182 to_screen('ERROR: the downloaded file hash does not match. Aborting.')
d5ed35b6
FV
183 return
184
185 try:
186 with open(filename, 'wb') as outf:
187 outf.write(newcontent)
0b63aed8 188 except (IOError, OSError):
5f6a1245 189 if verbose:
c0384f22 190 to_screen(encode_compat_str(traceback.format_exc()))
15938ab6 191 to_screen('ERROR: unable to overwrite current version')
d5ed35b6
FV
192 return
193
cefecac1 194 to_screen('Updated youtube-dlc. Restart youtube-dlc to use the new version.')
3bf79c75 195
5f6a1245 196
46a127ee 197def get_notes(versions, fromVersion):
3bf79c75 198 notes = []
5f6a1245 199 for v, vdata in sorted(versions.items()):
3bf79c75
PH
200 if v > fromVersion:
201 notes.extend(vdata.get('notes', []))
46a127ee
PH
202 return notes
203
5f6a1245 204
46a127ee
PH
205def print_notes(to_screen, versions, fromVersion=__version__):
206 notes = get_notes(versions, fromVersion)
3bf79c75 207 if notes:
15938ab6 208 to_screen('PLEASE NOTE:')
3bf79c75
PH
209 for note in notes:
210 to_screen(note)