# -*- coding: utf-8 -*-

from __future__ import print_function
try:
    from io import BytesIO
except:
    try:
        from cStringIO import StringIO as BytesIO
    except:
        from StringIO import StringIO as BytesIO
from setuptools import setup
from setuptools.command.build_ext import build_ext
from setuptools.extension import Extension
import os
import platform
import shutil
import struct
import sys
try:
    from urllib.request import urlopen, Request
except:
    from urllib2 import urlopen, Request
import zipfile
try:
    import xmlrpclib
except ImportError:
    import xmlrpc.client as xmlrpclib

package_dir = os.path.dirname(os.path.realpath(__file__))
pkg_info = os.path.join(package_dir, "PKG-INFO")
in_source_package = os.path.isfile(pkg_info)
if in_source_package:
    with open(pkg_info, "r") as f:
        version_line = [line.rstrip("\r") for line in f.read().split("\n") if line.startswith("Version: ")][0]
        frida_version = version_line[9:]
    long_description = None
else:
    root_dir = os.path.dirname(package_dir)
    frida_version = os.environ['FRIDA_VERSION']
    long_description = open(os.path.join(root_dir, "README.md")).read()
    frida_extension = os.environ['FRIDA_EXTENSION']

class UrllibTransport(xmlrpclib.Transport):
    def __init__(self, *args, **kwargs):
        xmlrpclib.Transport.__init__(self, *args, **kwargs)

    def request(self, host, handler, request_body, verbose=0):
        self.verbose = verbose
        scheme = "https"
        url = "%(scheme)s://%(host)s%(handler)s" % locals()
        req = Request(url, data=request_body, headers={'Content-Type': 'text/xml'})
        fp = urlopen(req)
        return self.parse_response(fp)

class FridaPrebuiltExt(build_ext):
    def build_extension(self, ext):
        target = self.get_ext_fullpath(ext.name)
        target_extension = os.path.splitext(target)[1]
        target_dir = os.path.dirname(target)
        try:
            os.makedirs(target_dir)
        except:
            pass
        if in_source_package:
            python_version = sys.version_info[0:2]
            python_major_version = python_version[0]
            system = platform.system()
            arch = struct.calcsize('P') * 8
            if system == 'Windows':
                os_version = "win-amd64" if arch == 64 else "win32"
            elif system == 'Darwin':
                os_version = "macosx-10.6-intel" if python_major_version == 3 else "macosx-10.11-intel"
            elif system == 'Linux':
                os_version = "linux-x86_64" if arch == 64 else "linux-i686"

            network_error = None

            try:
                print("querying pypi for available prebuilds")
                client = xmlrpclib.ServerProxy("https://pypi.python.org/pypi", transport=UrllibTransport())
                urls = client.release_urls("frida", frida_version)

                urls = [url for url in urls if url['python_version'] != 'source']

                def parse_version(version):
                    return tuple(map(int, version.split(".")))

                if python_major_version >= 3:
                    urls = [url for url in urls if parse_version(url['python_version'])[0] == python_major_version]
                else:
                    urls = [url for url in urls if parse_version(url['python_version']) == python_version]

                os_suffix = "-{}.egg".format(os_version)
                urls = [url for url in urls if url['filename'].endswith(os_suffix)]

                if len(urls) == 0:
                    raise Exception("Could not find prebuilt Frida extension. "
                                    "Prebuilds only provided for python 2.6-2.7 and 3.x.")

                url = urls[0]
                egg_filename = url['filename']
                egg_url = url['url']

                print("downloading prebuilt extension from", egg_url)
                egg_data = urlopen(egg_url).read()
            except Exception as e:
                network_error = e

            if network_error is not None:
                print("network query failed")

                egg_path = os.path.expanduser("~/frida-{}-py{}.{}-{}.egg".format(frida_version, python_version[0], python_version[1], os_version))
                print("looking for prebuilt extension in home directory, i.e.", egg_path)
                try:
                    with open(egg_path, "rb") as f:
                        egg_data = f.read()
                except:
                    print("no prebuilt extension found in home directory")
                    egg_data = None

                if egg_data is None:
                    raise network_error

            egg_file = BytesIO(egg_data)

            print("extracting prebuilt extension")
            egg_zip = zipfile.ZipFile(egg_file)
            extension_member = [info for info in egg_zip.infolist() if info.filename.endswith(target_extension)][0]
            extension_data = egg_zip.read(extension_member)
            with open(target, 'wb') as f:
                f.write(extension_data)
        else:
            shutil.copyfile(frida_extension, target)

setup(
    name='frida',
    version=frida_version,
    description="Inject JavaScript to explore native apps on Windows, macOS, Linux, iOS, Android, and QNX",
    long_description=long_description,
    author="Frida Developers",
    author_email="oleavr@nowsecure.com",
    url="https://www.frida.re",
    install_requires=[
        "colorama >= 0.2.7, < 1.0.0",
        "prompt-toolkit >= 0.57, < 2.0.0",
        "pygments >= 2.0.2, < 3.0.0"
    ],
    license="wxWindows Library Licence, Version 3.1",
    zip_safe=True,
    keywords="frida debugger inject javascript windows macos linux ios iphone ipad android",
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Environment :: Console",
        "Environment :: MacOS X",
        "Environment :: Win32 (MS Windows)",
        "Intended Audience :: Developers",
        "Intended Audience :: Science/Research",
        "License :: OSI Approved",
        "Natural Language :: English",
        "Operating System :: MacOS :: MacOS X",
        "Operating System :: Microsoft :: Windows",
        "Operating System :: POSIX :: Linux",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 3",
        "Programming Language :: Python :: Implementation :: CPython",
        "Programming Language :: JavaScript",
        "Topic :: Software Development :: Debuggers",
        "Topic :: Software Development :: Libraries :: Python Modules"
    ],
    packages=['frida'],
    entry_points={
        'console_scripts': [
            "frida = frida.repl:main",
            "frida-discover = frida.discoverer:main",
            "frida-ls-devices = frida.lsd:main",
            "frida-ps = frida.ps:main",
            "frida-kill = frida.kill:main",
            "frida-trace = frida.tracer:main"
        ]
    },
    ext_modules=[Extension('_frida', [])],
    cmdclass={
        'build_ext': FridaPrebuiltExt
    }
)
