From cb6ff87fbb05e421f77b57a79699c647866ceb09 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Wed, 26 Dec 2012 23:22:49 +0100 Subject: [PATCH] The new updates system, relies on gh-pages, secured by RSA, uses external web servers --- .gitignore | 1 + .../transition_helper_exe/youtube-dl.py | 63 ++++++++- youtube-dl | 57 +++++++- youtube-dl.exe | Bin 3790446 -> 3803016 bytes youtube_dl/__init__.py | 133 +++++++++++------- youtube_dl/utils.py | 28 ++++ 6 files changed, 224 insertions(+), 58 deletions(-) diff --git a/.gitignore b/.gitignore index f07fc5d60..564bde1d1 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ youtube-dl.exe youtube-dl.tar.gz .coverage cover/ +updates_key.pem diff --git a/devscripts/transition_helper_exe/youtube-dl.py b/devscripts/transition_helper_exe/youtube-dl.py index 409f980bc..dbb4c99e1 100644 --- a/devscripts/transition_helper_exe/youtube-dl.py +++ b/devscripts/transition_helper_exe/youtube-dl.py @@ -2,17 +2,48 @@ import sys, os import urllib2 +import json, hashlib + +def rsa_verify(message, signature, key): + from struct import pack + from hashlib import sha256 + from sys import version_info + def b(x): + if version_info[0] == 2: return x + else: return x.encode('latin1') + assert(type(message) == type(b(''))) + block_size = 0 + n = key[0] + while n: + block_size += 1 + n >>= 8 + signature = pow(int(signature, 16), key[1], key[0]) + raw_bytes = [] + while signature: + raw_bytes.insert(0, pack("B", signature & 0xFF)) + signature >>= 8 + signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes) + if signature[0:2] != b('\x00\x01'): return False + signature = signature[2:] + if not b('\x00') in signature: return False + signature = signature[signature.index(b('\x00'))+1:] + 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 + signature = signature[19:] + if signature != sha256(message).digest(): return False + return True sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n') sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n') -sys.stderr.write(u'The new location of the binaries is https://github.com/rg3/youtube-dl/downloads, not the git repository.\n\n') +sys.stderr.write(u'From now on, get the binaries from http://rg3.github.com/youtube-dl/download.html, not from the git repository.\n\n') raw_input() filename = sys.argv[0] -API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads" -EXE_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl.exe" +UPDATE_URL = "http://rg3.github.com/youtube-dl/update/" +VERSION_URL = UPDATE_URL + 'LATEST_VERSION' +JSON_URL = UPDATE_URL + 'versions.json' +UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537) if not os.access(filename, os.W_OK): sys.exit('ERROR: no write permissions on %s' % filename) @@ -23,13 +54,35 @@ sys.exit('ERROR: no write permissions on %s' % directory) try: - urlh = urllib2.urlopen(EXE_URL) + versions_info = urllib2.urlopen(JSON_URL).read().decode('utf-8') + versions_info = json.loads(versions_info) +except: + sys.exit(u'ERROR: can\'t obtain versions info. Please try again later.') +if not 'signature' in versions_info: + sys.exit(u'ERROR: the versions file is not signed or corrupted. Aborting.') +signature = versions_info['signature'] +del versions_info['signature'] +if not rsa_verify(json.dumps(versions_info, sort_keys=True), signature, UPDATES_RSA_KEY): + sys.exit(u'ERROR: the versions file signature is invalid. Aborting.') + +version = versions_info['versions'][versions_info['latest']] + +try: + urlh = urllib2.urlopen(version['exe'][0]) newcontent = urlh.read() urlh.close() +except (IOError, OSError) as err: + sys.exit('ERROR: unable to download latest version') + +newcontent_hash = hashlib.sha256(newcontent).hexdigest() +if newcontent_hash != version['exe'][1]: + sys.exit(u'ERROR: the downloaded file hash does not match. Aborting.') + +try: with open(exe + '.new', 'wb') as outf: outf.write(newcontent) except (IOError, OSError) as err: - sys.exit('ERROR: unable to download latest version') + sys.exit(u'ERROR: unable to write the new version') try: bat = os.path.join(directory, 'youtube-dl-updater.bat') diff --git a/youtube-dl b/youtube-dl index d5ca2d4ba..9766d905e 100755 --- a/youtube-dl +++ b/youtube-dl @@ -1,15 +1,44 @@ #!/usr/bin/env python import sys, os +import json, hashlib try: import urllib.request as compat_urllib_request except ImportError: # Python 2 import urllib2 as compat_urllib_request +def rsa_verify(message, signature, key): + from struct import pack + from hashlib import sha256 + from sys import version_info + def b(x): + if version_info[0] == 2: return x + else: return x.encode('latin1') + assert(type(message) == type(b(''))) + block_size = 0 + n = key[0] + while n: + block_size += 1 + n >>= 8 + signature = pow(int(signature, 16), key[1], key[0]) + raw_bytes = [] + while signature: + raw_bytes.insert(0, pack("B", signature & 0xFF)) + signature >>= 8 + signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes) + if signature[0:2] != b('\x00\x01'): return False + signature = signature[2:] + if not b('\x00') in signature: return False + signature = signature[signature.index(b('\x00'))+1:] + 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 + signature = signature[19:] + if signature != sha256(message).digest(): return False + return True + sys.stderr.write(u'Hi! We changed distribution method and now youtube-dl needs to update itself one more time.\n') sys.stderr.write(u'This will only happen once. Simply press enter to go on. Sorry for the trouble!\n') -sys.stderr.write(u'The new location of the binaries is https://github.com/rg3/youtube-dl/downloads, not the git repository.\n\n') +sys.stderr.write(u'From now on, get the binaries from http://rg3.github.com/youtube-dl/download.html, not from the git repository.\n\n') try: raw_input() @@ -18,19 +47,39 @@ except NameError: # Python 3 filename = sys.argv[0] -API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads" -BIN_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl" +UPDATE_URL = "http://rg3.github.com/youtube-dl/update/" +VERSION_URL = UPDATE_URL + 'LATEST_VERSION' +JSON_URL = UPDATE_URL + 'versions.json' +UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537) if not os.access(filename, os.W_OK): sys.exit('ERROR: no write permissions on %s' % filename) try: - urlh = compat_urllib_request.urlopen(BIN_URL) + versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8') + versions_info = json.loads(versions_info) +except: + sys.exit(u'ERROR: can\'t obtain versions info. Please try again later.') +if not 'signature' in versions_info: + sys.exit(u'ERROR: the versions file is not signed or corrupted. Aborting.') +signature = versions_info['signature'] +del versions_info['signature'] +if not rsa_verify(json.dumps(versions_info, sort_keys=True), signature, UPDATES_RSA_KEY): + sys.exit(u'ERROR: the versions file signature is invalid. Aborting.') + +version = versions_info['versions'][versions_info['latest']] + +try: + urlh = compat_urllib_request.urlopen(version['bin'][0]) newcontent = urlh.read() urlh.close() except (IOError, OSError) as err: sys.exit('ERROR: unable to download latest version') +newcontent_hash = hashlib.sha256(newcontent).hexdigest() +if newcontent_hash != version['bin'][1]: + sys.exit(u'ERROR: the downloaded file hash does not match. Aborting.') + try: with open(filename, 'wb') as outf: outf.write(newcontent) diff --git a/youtube-dl.exe b/youtube-dl.exe index a1187898699ba79c5e89d112dd5e1b7627720fb0..45eee04bbda9c816a80c467f0215512cd6bc95a6 100644 GIT binary patch delta 19629 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 delta 7397 zcmZ`;30zcF+dnhIzRHfE0uD3x!my~Maz{*Y-||&^altf!xxxU>5Hl!fy^6M&^=hu? zRWnmFOEW2rv9wa_t4|9N!Hvol6>;A)Q@`h)=Uhua-mA~g|9#H${Ga8Vd+wd%+y%{= zE1#U#RC#MOBNqE;QZ-eJ%^I_AiKM4Nl|j1L@dWV#@doh$@dfb%@dpV22?S{a5(E+q5&{wm5(W|u z5&;qk(iS8NBpRe0NPCbDATc1ZAaNiaU5y#~;YO{08&_hUzQS93Uqjay>y0yILJD=H z(2ZEMWR8Yt{<_yab?=7v?qm)@GY?{MCvCGmiN%9tdl8E#$@V6)h7|e`*^>cZGDk}s ztB6ACM=V~7K3s`nBW&EC%q$F0l%UHF0%}Ctj zwnUC1leMJChm=}0n(SyS9g2Z$ltMIQY{fV=^L9iIA{IY|IJ-TOAy#{0@n^Rkh}?m- z8R~^f;ct5~Sx02n4rPev2knbd#uW1ZpTfB(Qea(TDf~5SZI~xi@VpoVYtZ@#uHp18 zr`|QT5bpn3AjjI|X|cujyd0<1Zi}@~j&*`A*J{ftwaSiIt0T6+=`3-WQ&aP;&VsVs zlsx;C)YAO4)CzlxBqU7|a|o_{FSI!<-84KeoSu=_}LqUA&w+&;T#pHeGLermVEMz^Sm7 z$~hLN4*+1#v)i1q&FOT9+q`1CLmuS;h?zl;CevXv^sy1_w$N_1F~Zy&r&9w)xpeCc zy_C=!V5G)R+eQO_w4Pdjt%t@#8>R`>25W*f%D*~IpvF(*Lo;92w|7l?S^vG(f5cFo zTPQeskukn=UH-H5KY8mE^vj$2bRUMom*~sA7+UzQKF))oFV^Zm(lIpYL;amlhQ{sC ze;&e6K&?L4ogqhq{s-jCIIjNxT)o)Nz{oS{+A#~+MfNGOW$kipQP4e_rBGE`R`f6tSlns4Ikk{DWZF@8B# z)bmb!?m(tn+tcuy8$+}E7_Mj-a`ZDyL|wxmLwy^j+c?hfF6stPG-M&mngT;xtmEq< z!*`hT9Gl@bW;){)!%u$9ckn!e7FmX`FchK3&Q*p?R>!h!hFQ#N&Nf3BMp(Jsuo3$` zXt$veISl&^pI}ED>J1uS=5h46VIM|af6CAWc^jJyb5SRsH!MT+@PfgN%ss9c%5~l_ zyP0Qyt{<6C+w?I;xazMP%5ix39YYH@yl+^I=(k6Pm564z8UM%4UvV}$76vVW<$Ide z1slU%dEUl#ZjANQ37IF}rj>5SDEjD_L8I3p% zK7EW!&~VKF<0-cQCI4p6qMq*u5F*6ToNQwVt^HNGf|f*R+cUGaM3Ohi{cH>RUq!fs=4M8&nn&4}6^Hx57~Uo!3p zVBH&SNNI>r@^9r!gJWDKLLPeOyoMhcvIrBUE*9d8mo zz=k|%2n^1lncIahSNl$aQ_G_CJ=51Q36)#A2o6+E`smOn+_HC9uw3D?7p<~d?Cs;cB-%v2Q>B|vKa;@;y~04VJFV- zv6;diMDM*S96T z6;3!L9L3S}MxhB&%n9KTqPu5=HHcO=3pI#}E(?4|9C|}|7x&PXd%{T^t$8S%LsYF3 zPa#_2C2qsGBYj1l^X5RY2z{d?#BsP^P3tJu;>-^=ieICLcY+v>YoKF_xEOWYJBe!% zO-NIbwwt&Kbyeviujs9w;%?Oaqqq1OB3)nc3q(8mi{m}mil6gv&z#rktnp%7SNbq< zIWFm@piS?;hRU16#s8qs{E=cO%zF1&u^M&L$Ezr9qWCfDPD~OvB6>SdJdF9vRl<4 z!PQKx9Zan4yCtPxX$ErObr7=SHI_}l0j^|Te$C)>)nsaqh4`v*HPKIn3Kzb(k|>KP>fU?$QySM#T|M_>Bs5N4eL4qulF_qddf375E&@R|V@-(4+#xZ`@|G3O1;qSp^Bl zxlN%8HmTr}3X)H78=DHYsNk9kI-le=r7GB}f?F!+dWzdjRe?(dzn|h$(EGI1pS@

{NoJQEIh;4!hth<>cW~h$Zq0`;y+DNPxj#> zU8#(u@0^pu>CGldWYitb^0>-JkxU<*g}9Yxr9?LP=~-zI1NXp+fy{H#KnC7AC-r5Z z?i>#nex7SyP{CRiG^-$`ncFy3@KrN+e*}OvVCx?5FW;cMnjw)FFGvCl^5z9@wO<7x zEnG8J1*=qWx`nq}e~~vyR>6jgynihhc_N)I@yg0ouuBDAmwAZiRIuo>)Q{zJ>@s(c zxx#HMS2)inSGdj1EBv8KyUNojyUL?)zsjG?UMo(GJ&PBL8(=Vn0ji=Ihb`#&G94k0RaRx&7+~pS1tIAq{3W zp*MLH{HR%T6QaCylSf&3llz@it-6QLZ+Vq6tDd(cf#o;i-ub;;Hyd<}_LEz@^OseS za+`BGZu62|w|VOx+~%8Uw>x|bw%y@7@5gue=5Ts7Ek5R!pD%g8hdPa%9?00S= zsi5ed6x}0#t2-%@E2h~?Eh!}x_7Ouh+EDWNq#|>uutRDCXC*tm3Gs)IdG)HUT;!s= zfq)fu0)Vz=C;juD6q%s>gy@&w&?_tfKC`D1M)sH6vWM=Z*q)znwdK>1ho!d6f7~BZ zwAXKf2bp4>BGZL;q_9|p<=(2_9bbkJ5|Z+C@PldeGw@-~wE(o$1L>tdq)1V5n~`kX zaT5-a7C_$cOYZSiUAcRV2eCQfT&N4Z_JdKSZbkco^ zotMxV_oZmhbT}-vmpG~2-5l0kiR3Y3+pv0Q)>qvLQI3(+tGe>?3NUrF(ouQ=n0%VH zD8pUkDdjAYbcO8mj&-50N`%BMCqy~fQm^XDq17Iw#FE<@TlSw)wE5Om7`NsX70a!d zWM+i;J+mI>ZeNF|yQphVw}3zEVhw<-`b1AsQc?=%Uab**G4a6S0BF(cQ1O$U`XJqt zyi{ff&sO|s&j(WU$i_aNq%_B7u}^8mB(tw+^9U<&4uDB`vXQqZfM08M(T{;PdUk;_ zTvFN!DLnxO+RPIA=L0FytaR8hJH7ItFBA|(2+a87tGe>T1)juFP%M|X#xwjO@T^`* ziylhsla$T}#i)U6z{Q?dB)8tH`@P(nA;5SzIDB$4pL2m<>lNKs0%dQfk&mSIY;FEH zHmY2Jwj@DYloMg~s;;cvN3$MDF-E1VWkV|Vz5zO_O90G@_c4(8A8WsMnu6)+ri#VJ)trfkry*YZ^r(Vi9 zETQ~XTA8VPdFq(Na6(SNiSv_>X74D~uLS0^72l-`&C%LrotA!TW>4hOz2@lgqdJ3A zS#C+GJx_MD_OxX#Z>gV`2J3Yl1oV5NkWFK}h*O?YGTGYdV?%psAf8t-eL-W6Voyi~ zD0(g~1Fr(*K>5E^cwu&NcO%UB5Ge1-HTKFfTBk9y!&G_^xU%=H0xo-zY_m>pZGmUT z44va!0L%4@=%?rPwJPu|&B>E=@p%%h2$f5(pR+Rh(u*Viz`kZ|2OQ3+u-aY z?1?@M+2uvbY*wdJb~yjqksB2PpYP~ht(pBwq-}NPXx*7+FB-QXDU=S?nc1%?nysk& z|L#T0b!PTMkS+&}ww*hz)|um2-x?J~K>)q0Dx%#$;U7uU-OTLAI-Q^>u0_-7Zf1PI zRw;^GBK^vZNB-b++S%QVZ<^=a zfjMeDEp#_Wu`T>r_o5dfV2QsO`1HH&YpuIGfFz^K*WP4WPN@ynL8~ioa`VNe zG+1)e;7NNjpNjo7*u&gicc{^u4pTtEHDCI=hq;^fvkdx;hxr-pxR2;94|AIKVH{2H b1pTOPG{@82HlT&HkU;n^m%&DOhRFW`{d@xE diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index d12ece21e..cc2f555b9 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -41,47 +41,90 @@ from .InfoExtractors import * from .PostProcessor import * -def updateSelf(downloader, filename): +def update_self(to_screen, verbose, filename): """Update the program file with the latest version from the repository""" - # TODO: at least, check https certificates - from zipimport import zipimporter + import json, traceback, hashlib - API_URL = "https://api.github.com/repos/rg3/youtube-dl/downloads" - BIN_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl" - EXE_URL = "https://github.com/downloads/rg3/youtube-dl/youtube-dl.exe" + UPDATE_URL = "http://rg3.github.com/youtube-dl/update/" + VERSION_URL = UPDATE_URL + 'LATEST_VERSION' + JSON_URL = UPDATE_URL + 'versions.json' + UPDATES_RSA_KEY = (0x9d60ee4d8f805312fdb15a62f87b95bd66177b91df176765d13514a0f1754bcd2057295c5b6f1d35daa6742c3ffc9a82d3e118861c207995a8031e151d863c9927e304576bc80692bc8e094896fcf11b66f3e29e04e3a71e9a11558558acea1840aec37fc396fb6b65dc81a1c4144e03bd1c011de62e3f1357b327d08426fe93, 65537) - if hasattr(sys, "frozen"): # PY2EXE - if not os.access(filename, os.W_OK): - sys.exit('ERROR: no write permissions on %s' % filename) - downloader.to_screen(u'Updating to latest version...') + if not isinstance(globals().get('__loader__'), zipimporter) and not hasattr(sys, "frozen"): + to_screen(u'It looks like you installed youtube-dl with pip, setup.py or a tarball. Please use that to update.') + return - urla = compat_urllib_request.urlopen(API_URL) - download = filter(lambda x: x["name"] == "youtube-dl.exe", json.loads(urla.read())) - if not download: - downloader.to_screen(u'ERROR: can\'t find the current version. Please try again later.') - return - newversion = download[0]["description"].strip() - if newversion == __version__: - downloader.to_screen(u'youtube-dl is up-to-date (' + __version__ + ')') - return - urla.close() + # Check if there is a new version + try: + newversion = compat_urllib_request.urlopen(VERSION_URL).read().decode('utf-8').strip() + except: + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: can\'t find the current version. Please try again later.') + return + if newversion == __version__: + to_screen(u'youtube-dl is up-to-date (' + __version__ + ')') + return + # Download and check versions info + try: + versions_info = compat_urllib_request.urlopen(JSON_URL).read().decode('utf-8') + versions_info = json.loads(versions_info) + except: + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: can\'t obtain versions info. Please try again later.') + return + if not 'signature' in versions_info: + to_screen(u'ERROR: the versions file is not signed or corrupted. Aborting.') + return + signature = versions_info['signature'] + del versions_info['signature'] + if not rsa_verify(json.dumps(versions_info, sort_keys=True), signature, UPDATES_RSA_KEY): + to_screen(u'ERROR: the versions file signature is invalid. Aborting.') + return + + to_screen(u'Updating to version ' + versions_info['latest'] + '...') + version = versions_info['versions'][versions_info['latest']] + if version.get('notes'): + to_screen(u'PLEASE NOTE:') + for note in version['notes']: + to_screen(note) + + if not os.access(filename, os.W_OK): + to_screen(u'ERROR: no write permissions on %s' % filename) + return + + # Py2EXE + if hasattr(sys, "frozen"): exe = os.path.abspath(filename) directory = os.path.dirname(exe) if not os.access(directory, os.W_OK): - sys.exit('ERROR: no write permissions on %s' % directory) + to_screen(u'ERROR: no write permissions on %s' % directory) + return try: - urlh = compat_urllib_request.urlopen(EXE_URL) + urlh = compat_urllib_request.urlopen(version['exe'][0]) newcontent = urlh.read() urlh.close() + except (IOError, OSError) as err: + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: unable to download latest version') + return + + newcontent_hash = hashlib.sha256(newcontent).hexdigest() + if newcontent_hash != version['exe'][1]: + to_screen(u'ERROR: the downloaded file hash does not match. Aborting.') + return + + try: with open(exe + '.new', 'wb') as outf: outf.write(newcontent) except (IOError, OSError) as err: - sys.exit('ERROR: unable to download latest version') + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: unable to write the new version') + return try: bat = os.path.join(directory, 'youtube-dl-updater.bat') @@ -96,43 +139,35 @@ def updateSelf(downloader, filename): os.startfile(bat) except (IOError, OSError) as err: - sys.exit('ERROR: unable to overwrite current version') - - elif isinstance(globals().get('__loader__'), zipimporter): # UNIX ZIP - if not os.access(filename, os.W_OK): - sys.exit('ERROR: no write permissions on %s' % filename) - - downloader.to_screen(u'Updating to latest version...') - - urla = compat_urllib_request.urlopen(API_URL) - download = [x for x in json.loads(urla.read().decode('utf8')) if x["name"] == "youtube-dl"] - if not download: - downloader.to_screen(u'ERROR: can\'t find the current version. Please try again later.') + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: unable to overwrite current version') return - newversion = download[0]["description"].strip() - if newversion == __version__: - downloader.to_screen(u'youtube-dl is up-to-date (' + __version__ + ')') - return - urla.close() + # Zip unix package + elif isinstance(globals().get('__loader__'), zipimporter): try: - urlh = compat_urllib_request.urlopen(BIN_URL) + urlh = compat_urllib_request.urlopen(version['bin'][0]) newcontent = urlh.read() urlh.close() except (IOError, OSError) as err: - sys.exit('ERROR: unable to download latest version') + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: unable to download latest version') + return + + newcontent_hash = hashlib.sha256(newcontent).hexdigest() + if newcontent_hash != version['bin'][1]: + to_screen(u'ERROR: the downloaded file hash does not match. Aborting.') + return try: with open(filename, 'wb') as outf: outf.write(newcontent) except (IOError, OSError) as err: - sys.exit('ERROR: unable to overwrite current version') + if verbose: to_screen(traceback.format_exc().decode()) + to_screen(u'ERROR: unable to overwrite current version') + return - else: - downloader.to_screen(u'It looks like you installed youtube-dl with pip or setup.py. Please use that to update.') - return - - downloader.to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.') + to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.') def parseOpts(): def _readOptions(filename_bytes): @@ -578,7 +613,7 @@ def _real_main(): # Update version if opts.update_self: - updateSelf(fd, sys.argv[0]) + update_self(fd.to_screen, opts.verbose, sys.argv[0]) # Maybe do nothing if len(all_urls) < 1: diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index 463804e18..7d6041929 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -410,6 +410,34 @@ def encodeFilename(s): else: return s.encode(sys.getfilesystemencoding(), 'ignore') +def rsa_verify(message, signature, key): + from struct import pack + from hashlib import sha256 + from sys import version_info + def b(x): + if version_info[0] == 2: return x + else: return x.encode('latin1') + assert(type(message) == type(b(''))) + block_size = 0 + n = key[0] + while n: + block_size += 1 + n >>= 8 + signature = pow(int(signature, 16), key[1], key[0]) + raw_bytes = [] + while signature: + raw_bytes.insert(0, pack("B", signature & 0xFF)) + signature >>= 8 + signature = (block_size - len(raw_bytes)) * b('\x00') + b('').join(raw_bytes) + if signature[0:2] != b('\x00\x01'): return False + signature = signature[2:] + if not b('\x00') in signature: return False + signature = signature[signature.index(b('\x00'))+1:] + 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 + signature = signature[19:] + if signature != sha256(message).digest(): return False + return True + class DownloadError(Exception): """Download Error exception.