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.

172 lines
6.5 KiB

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