Created
January 28, 2018 06:34
-
-
Save ellieayla/299400ba2cff4e7b3a460c06835036c3 to your computer and use it in GitHub Desktop.
Direct download url for the latest PyCharm Community Edition.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
The JetBrains Toolbox app fetches (xz-compressed) json feeds inside ASN.1 signedData. | |
Retrieve the feed and locate the direct download url for the latest PyCharm Community Edition. | |
""" | |
from __future__ import print_function | |
import json | |
import platform | |
from distutils.version import LooseVersion | |
from pyasn1.codec.der.decoder import decode as der_decoder | |
from pyasn1_modules.rfc2315 import ContentInfo, contentTypeMap, signedData, data | |
try: | |
import lzma | |
except ImportError: | |
from backports import lzma | |
try: | |
from urllib.request import urlopen | |
except ImportError: | |
from urllib2 import urlopen | |
# This constant is hardcoded in "JetBrains Toolbox.app" | |
PUBLIC_FEED = 'https://download.jetbrains.com/toolbox/feeds/v1/public-feed.feed.xz.signed' | |
RELEASE_FEED = 'release.feed' | |
CURRENT_PLATFORM = {'Windows': 'windows', 'Linux': 'linux', 'Darwin': 'mac'}[platform.system()] | |
def asn1_to_signed_data(filelikeobject): | |
contentInfo, rest_of_input = der_decoder(filelikeobject.read(), asn1Spec=ContentInfo()) | |
# Signed Data | |
contentType = contentInfo.getComponentByName('contentType') | |
assert contentType == signedData | |
contentInfo, leftover = der_decoder( | |
contentInfo.getComponentByName('content'), | |
asn1Spec=contentTypeMap[contentType] | |
) | |
# data | |
data_contentInfo = contentInfo.getComponentByName("contentInfo") | |
data_contentType = data_contentInfo.getComponentByName('contentType') | |
assert data_contentType == data | |
raw_xz_data, leftover = der_decoder( | |
data_contentInfo.getComponentByName('content'), | |
asn1Spec=contentTypeMap[data_contentType] | |
) | |
# bytes | |
return raw_xz_data._value | |
def decompress_xz_json(bytesequence): | |
json_string = lzma.LZMADecompressor().decompress(bytesequence) | |
return json.loads(json_string) | |
def locate_latest_package_url(d, platform_os=CURRENT_PLATFORM): | |
available_packages = [n for n in d['entries'] if n['id'] == 'PyCharm-C' and n['package']['os'] == platform_os] | |
newest_package = sorted(available_packages, key=lambda a: LooseVersion(a['version']), reverse=True)[0] | |
return newest_package['package']['url'] | |
def toolbox(): | |
keyfile = urlopen(PUBLIC_FEED) | |
raw_xz_data = asn1_to_signed_data(keyfile) | |
feed_url = [f['url'] for f in decompress_xz_json(raw_xz_data)['feeds'] if RELEASE_FEED in f['url']][0] | |
keyfile = urlopen(feed_url) | |
raw_xz_data = asn1_to_signed_data(keyfile) | |
packages = decompress_xz_json(raw_xz_data) | |
url = locate_latest_package_url(packages) | |
return url | |
if __name__ == "__main__": | |
print(toolbox()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment