Skip to content

Instantly share code, notes, and snippets.

@ellieayla
Created January 28, 2018 06:34
Show Gist options
  • Save ellieayla/299400ba2cff4e7b3a460c06835036c3 to your computer and use it in GitHub Desktop.
Save ellieayla/299400ba2cff4e7b3a460c06835036c3 to your computer and use it in GitHub Desktop.
Direct download url for the latest PyCharm Community Edition.
#!/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