#!/usr/bin/env python3
"""Downloads the latest NPAPI or PPAPI Flash plugin directly from Adobe."""
# Copyright (c) 2014, 2016-2017 Scott Zeid.
# Released under the X11 License:
#
import argparse
import datetime
import enum
import html
import io
import json
import os
import platform
import re
import sys
import tarfile
import time
import urllib.parse
import urllib.request
class APIs(enum.Enum):
PPAPI = 1 #enum.auto()
NPAPI = 2 #enum.auto()
class MainError(Exception):
pass
class NamedBytesIO(io.BytesIO):
name = None
def main(argv):
try:
hep_easter_egg = len(argv) >= 2 and argv[1] == "--hep"
if not hep_easter_egg:
p = argparse.ArgumentParser(
description=__doc__.splitlines()[0].rstrip(),
)
p.add_argument("--hep", dest="_hep_easter_egg", action="store_true",
help=argparse.SUPPRESS)
p.add_argument("-a", "--arch", "--architecture", choices=["x86_64", "i686"],
help="download for the given architecture")
g = p.add_argument_group("APIs - exactly one of the following is required")
m = g.add_mutually_exclusive_group(required=True)
m.add_argument("-n", "--npapi",
action="store_const", dest="api", const=APIs.NPAPI,
help="download the NPAPI version (for use with Firefox and related"
" browsers)")
m.add_argument("-p", "--ppapi", "--pepper",
action="store_const", dest="api", const=APIs.PPAPI,
help="download the PPAPI version (for use with Chrome and related"
" browsers)")
g = p.add_argument_group("output options - at least one of the following is required")
g.add_argument("-o", "--output", metavar="OUTPUT_TO",
help="save the .tar.gz archive to the given filename or directory")
g.add_argument("-e", "--extract", metavar="EXTRACT_TO",
help="extract the .tar.gz archive to the given directory"
" (will be created if necessary)")
try:
args = p.parse_args(argv[1:])
except SystemExit as exc:
return exc.code
if not args.output and not args.extract:
try:
p.error("at least one of the following are required: -o/--output, -e/--extract")
except SystemExit as exc:
return exc.code
hep_easter_egg = args._hep_easter_egg
if hep_easter_egg:
print("Hep! Hep! I'm covered in sawlder! ... Eh? Nobody comes.")
print("--Red Green, https://www.youtube.com/watch?v=qVeQWtVzkAQ#t=6m27s")
return 0
archive = get_flash(args.api, args.arch)
if args.output:
filename = args.output
if os.path.isdir(filename):
filename = os.path.join(filename, archive.name)
with open(filename, "wb") as f:
f.write(archive.read())
archive.seek(0)
if args.extract:
if not os.path.exists(args.extract):
os.makedirs(args.extract)
if not os.path.isdir(args.extract):
raise MainError("extract-to path exists but is not a directory")
tf = tarfile.open(mode="r", fileobj=archive)
tf.extractall(args.extract)
tf.close()
archive.seek(0)
return 0
except MainError as e:
print("get-flash: error: " + str(e), file=sys.stderr)
return 1
def get_flash(api=None, arch=None):
api = getattr(api, "name", api).upper()
if api not in APIs.__members__.keys():
raise ValueError("unsupported API: %s" % api)
api = api.lower()
if arch is None:
arch = platform.processor()
if arch == "x86_64" or arch == "x86-64":
arch = "x86-64"
elif re.search(r"^i[0-9]86$", arch) or arch == "x86-32":
arch = "x86-32"
else:
raise MainError("unsupported architecture: %s" % arch)
url_landing = "https://get.adobe.com/flashplayer/otherversions/"
request(url_landing)
url_installers = "https://get.adobe.com/flashplayer/webservices/json/?platform_type=Linux&platform_dist=&platform_arch=%s&browser_arch=&browser_type=&browser_vers=&browser_dist=&eventname=flashplayerotherversions"
url_installers %= arch
time.sleep(0.463)
installers = json.loads(request(url_installers, referer=url_landing).decode("utf-8"))
url_download = name = None
for i in installers:
if api in i["download_url"] and ".tar.gz" in i["download_url"]:
url_download = i["download_url"]
name = i["queryName"]
break
if not url_download:
raise MainError("could not find %s plugin download URL" % api.upper())
url_confirm = "https://get.adobe.com/flashplayer/download/?installer=%s&standalone=1"
url_confirm %= html.escape(name)
time.sleep(0.361)
request(url_confirm, referer=url_installers)
time.sleep(0.883)
result = NamedBytesIO(request(url_download, referer=url_confirm))
result.name = os.path.basename(urllib.parse.urlparse(url_download).path)
return result
def request(url, referer=None):
req = urllib.request.Request(url)
req.add_header("User-Agent", user_agent())
if referer:
req.add_header("Referer", referer)
with urllib.request.urlopen(req) as f:
return f.read()
def firefox_version(today=None):
"""Returns (approximately) the latest major version of Firefox."""
if today != None and not isinstance(today, datetime.date):
raise TypeError("today must be a datetime.date")
epoch_date = datetime.date(2014, 2, 4)
epoch_version = 27
today = today or datetime.date.today()
days = (today - epoch_date).days
weeks = days // 7
cycles = weeks // 6
return epoch_version + cycles
def user_agent(today=None):
"""Returns a user agent string for (approximately) the latest major version of Firefox."""
tpl = "Mozilla/5.0 (X11; Linux x86_64; rv:{v}) Gecko/20100101 Firefox/{v}"
version = str(firefox_version(today)) + ".0"
return tpl.format(v=version)
if __name__ == "__main__":
try:
sys.exit(main(sys.argv))
except KeyboardInterrupt:
pass