You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

183 lines
6.9 KiB

11 years ago
11 years ago
11 years ago
  1. import io
  2. import json
  3. import traceback
  4. import hashlib
  5. import subprocess
  6. import sys
  7. from zipimport import zipimporter
  8. from .utils import *
  9. from .version import __version__
  10. def rsa_verify(message, signature, key):
  11. from struct import pack
  12. from hashlib import sha256
  13. from sys import version_info
  14. def b(x):
  15. if version_info[0] == 2: return x
  16. else: return x.encode('latin1')
  17. assert(type(message) == type(b('')))
  18. block_size = 0
  19. n = key[0]
  20. while n:
  21. block_size += 1
  22. n >>= 8
  23. signature = pow(int(signature, 16), key[1], key[0])
  24. raw_bytes = []
  25. while signature:
  26. raw_bytes.insert(0, pack("B", signature & 0xFF))
  27. signature >>= 8
  28. signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes)
  29. if signature[0:2] != b('\x00\x01'): return False
  30. signature = signature[2:]
  31. if not b('\x00') in signature: return False
  32. signature = signature[signature.index(b('\x00'))+1:]
  33. if not signature.startswith(b('\x30\x31\x30\x0D\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20')): return False
  34. signature = signature[19:]
  35. if signature != sha256(message).digest(): return False
  36. return True
  37. def update_self(to_screen, verbose):
  38. """Update the program file with the latest version from the repository"""
  39. UPDATE_URL = "http://rg3.github.io/youtube-dl/update/"
  40. VERSION_URL = UPDATE_URL + 'LATEST_VERSION'
  41. JSON_URL = UPDATE_URL + 'versions.json'
  42. UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537)
  43. if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, "frozen"):
  44. to_screen(u'It looks like you installed youtube-dl with a package manager, pip, setup.py or a tarball. Please use that to update.')
  45. return
  46. # Check if there is a new version
  47. try:
  48. newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip()
  49. except:
  50. if verbose: to_screen(compat_str(traceback.format_exc()))
  51. to_screen(u'ERROR: can\'t find the current version. Please try again later.')
  52. return
  53. if newversion == __version__:
  54. to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
  55. return
  56. # Download and check versions info
  57. try:
  58. versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8')
  59. versions_info = json.loads(versions_info)
  60. except:
  61. if verbose: to_screen(compat_str(traceback.format_exc()))
  62. to_screen(u'ERROR: can\'t obtain versions info. Please try again later.')
  63. return
  64. if not 'signature' in versions_info:
  65. to_screen(u'ERROR: the versions file is not signed or corrupted. Aborting.')
  66. return
  67. signature = versions_info['signature']
  68. del versions_info['signature']
  69. if not rsa_verify(json.dumps(versions_info, sort_keys=True).encode('utf-8'), signature, UPDATES_RSA_KEY):
  70. to_screen(u'ERROR: the versions file signature is invalid. Aborting.')
  71. return
  72. version_id = versions_info['latest']
  73. to_screen(u'Updating to version ' + version_id + '...')
  74. version = versions_info['versions'][version_id]
  75. print_notes(to_screen, versions_info['versions'])
  76. filename = sys.argv[0]
  77. # Py2EXE: Filename could be different
  78. if hasattr(sys, "frozen") and not os.path.isfile(filename):
  79. if os.path.isfile(filename + u'.exe'):
  80. filename += u'.exe'
  81. if not os.access(filename, os.W_OK):
  82. to_screen(u'ERROR: no write permissions on %s' % filename)
  83. return
  84. # Py2EXE
  85. if hasattr(sys, "frozen"):
  86. exe = os.path.abspath(filename)
  87. directory = os.path.dirname(exe)
  88. if not os.access(directory, os.W_OK):
  89. to_screen(u'ERROR: no write permissions on %s' % directory)
  90. return
  91. try:
  92. urlh = compat_urllib_request.urlopen(version['exe'][0])
  93. newcontent = urlh.read()
  94. urlh.close()
  95. except (IOError, OSError) as err:
  96. if verbose: to_screen(compat_str(traceback.format_exc()))
  97. to_screen(u'ERROR: unable to download latest version')
  98. return
  99. newcontent_hash = hashlib.sha256(newcontent).hexdigest()
  100. if newcontent_hash != version['exe'][1]:
  101. to_screen(u'ERROR: the downloaded file hash does not match. Aborting.')
  102. return
  103. try:
  104. with open(exe + '.new', 'wb') as outf:
  105. outf.write(newcontent)
  106. except (IOError, OSError) as err:
  107. if verbose: to_screen(compat_str(traceback.format_exc()))
  108. to_screen(u'ERROR: unable to write the new version')
  109. return
  110. try:
  111. bat = os.path.join(directory, 'youtube-dl-updater.bat')
  112. with io.open(bat, 'w') as batfile:
  113. batfile.write(u"""
  114. @echo off
  115. echo Waiting for file handle to be closed ...
  116. ping 127.0.0.1 -n 5 -w 1000 > NUL
  117. move /Y "%s.new" "%s" > NUL
  118. echo Updated youtube-dl to version %s.
  119. start /b "" cmd /c del "%%~f0"&exit /b"
  120. \n""" % (exe, exe, version_id))
  121. subprocess.Popen([bat]) # Continues to run in the background
  122. return # Do not show premature success messages
  123. except (IOError, OSError) as err:
  124. if verbose: to_screen(compat_str(traceback.format_exc()))
  125. to_screen(u'ERROR: unable to overwrite current version')
  126. return
  127. # Zip unix package
  128. elif isinstance(globals().get('__loader__'), zipimporter):
  129. try:
  130. urlh = compat_urllib_request.urlopen(version['bin'][0])
  131. newcontent = urlh.read()
  132. urlh.close()
  133. except (IOError, OSError) as err:
  134. if verbose: to_screen(compat_str(traceback.format_exc()))
  135. to_screen(u'ERROR: unable to download latest version')
  136. return
  137. newcontent_hash = hashlib.sha256(newcontent).hexdigest()
  138. if newcontent_hash != version['bin'][1]:
  139. to_screen(u'ERROR: the downloaded file hash does not match. Aborting.')
  140. return
  141. try:
  142. with open(filename, 'wb') as outf:
  143. outf.write(newcontent)
  144. except (IOError, OSError) as err:
  145. if verbose: to_screen(compat_str(traceback.format_exc()))
  146. to_screen(u'ERROR: unable to overwrite current version')
  147. return
  148. to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
  149. def get_notes(versions, fromVersion):
  150. notes = []
  151. for v,vdata in sorted(versions.items()):
  152. if v > fromVersion:
  153. notes.extend(vdata.get('notes', []))
  154. return notes
  155. def print_notes(to_screen, versions, fromVersion=__version__):
  156. notes = get_notes(versions, fromVersion)
  157. if notes:
  158. to_screen(u'PLEASE NOTE:')
  159. for note in notes:
  160. to_screen(note)