# This is a sample Python script.

# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
import os.path
import re
import urllib.request
import urllib.error
import urllib.parse
import time
import logging
import os
import base64

gfwlist_url = 'https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt'


def decode_gfwlist(content):
    # decode base64 if have to
    try:
        return str(base64.b64decode(content),'utf-8')
    except Exception:
        return content


def get_hostname(something):
    try:
        # quite enough for GFW
        if not (something.startswith('http:') or something.startswith('https:')):
            something = 'http://' + something
        r = urllib.parse.urlparse(something)
        return r.hostname
    except Exception as e:
        logging.error(e)
        return None


def add_domain_to_set(s, something):
    hostname = get_hostname(something)
    if hostname is not None:
        if hostname.startswith('.'):
            hostname = hostname.lstrip('.')
        if hostname.endswith('/'):
            hostname = hostname.rstrip('/')
        if hostname:
            s.add(hostname)


def parse_gfwlist(content, user_rule=None):
    # builtin_rules = pkgutil.get_data('gfwlist2privoxy', 'resources/builtin.txt').splitlines(False)
    builtin_rules = ""
    gfwlist = content.splitlines(False)
    if user_rule:
        gfwlist.extend(user_rule.splitlines(False))
    domains = set(builtin_rules)
    for line in gfwlist:
        if line.find('.*') >= 0:
            continue
        elif line.find('*') >= 0:
            line = line.replace('*', '/')
        if line.startswith('!'):
            continue
        elif line.startswith('['):
            continue
        elif line.startswith('@'):
            # ignore white list
            continue
        elif line.startswith('||'):
            add_domain_to_set(domains, line.lstrip('||'))
        elif line.startswith('|'):
            add_domain_to_set(domains, line.lstrip('|'))
        elif line.startswith('.'):
            add_domain_to_set(domains, line.lstrip('.'))
        else:
            add_domain_to_set(domains, line)
    return domains


def reduce_domains(domains):
    # reduce 'www.google.com' to 'google.com'
    # remove invalid domains
    # tld_content = pkgutil.get_data('gfwlist2privoxy', 'resources/tld.txt')
    # tld_content = ""
    tld_content = open('tld.txt','rb').read()
    tld_content = str(tld_content,'utf-8')
    tlds = set(tld_content.splitlines(False))
    new_domains = set()
    for domain in domains:
        domain_parts = domain.split('.')
        last_root_domain = None
        for i in range(0, len(domain_parts)):
            root_domain = '.'.join(domain_parts[len(domain_parts) - i - 1:])
            if i == 0:
                if not tlds.__contains__(root_domain):
                    # root_domain is not a valid tld
                    break
            last_root_domain = root_domain
            if tlds.__contains__(root_domain):
                continue
            else:
                break
        if last_root_domain is not None:
            new_domains.add(last_root_domain)
    return new_domains


def generate_action(domains, proxy, proxy_type):
    # render the action file
    # proxy_content = pkgutil.get_data('gfwlist2privoxy', 'resources/gfwlist.action')
    proxy_content = '# gfwlist.action generated by gfwlist2privoxy, __TIME__ \n' \
                    '# Original author : https://github.com/snachx/gfwlist2privoxy \n' \
                    '\n' \
                    '{+forward-override{__FORWARDER__}}\n' \
                    '__DOMAINS__\n'
    if proxy_type == 'http':
        forward_string = 'forward ' + proxy
    else:
        forward_type = 'forward' + '-' + proxy_type
        forward_string = forward_type + ' ' + proxy + ' .'
    proxy_content = proxy_content.replace('__FORWARDER__', forward_string)
    domains_string = ''
    for domain in domains:
        domains_string += '.' + domain + '\n'
    proxy_content = proxy_content.replace('__DOMAINS__', domains_string)
    gen_time = time.localtime()
    format_time = time.strftime("%Y-%m-%d %X %z", gen_time)
    proxy_content = proxy_content.replace('__TIME__', format_time)
    return proxy_content


def is_url(input):
    # URL validator copied from django
    regex = re.compile(
        r'^(?:http|ftp)s?://'  # http:// or https://
        r'(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|'  # domain...
        r'localhost|'  # localhost...
        r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|'  # ...or ipv4
        r'\[?[A-F0-9]*:[A-F0-9:]+]?)'  # ...or ipv6
        r'(?::\d+)?'  # optional port
        r'(?:/?|[/?]\S+)$', re.IGNORECASE)
    return regex.match(input)


def main():
    # Change to your local socks address
    socks_proxy = "127.0.0.1:1089"
    user_rule = None
    if os.path.exists('pac.txt') and ((time.time() - os.path.getmtime('pac.txt')) < 86400.0):
        content = open('pac.txt','rb').read()
        print('[info] : Local pac file is the newest. Read from local file')
    else:
        print('Downloading gfwlist from %s' % gfwlist_url)
        content = urllib.request.urlopen(gfwlist_url, timeout=10).read()
        print('Download Finished! Saved(or overwrite) as pac.txt')
        with open('pac.txt','wb') as fid:
            fid.write(content)
    content = decode_gfwlist(content)
    domains = parse_gfwlist(content, user_rule)
    domains = reduce_domains(domains)
    # socks5 can be any proxy protocols that privoxy supports
    pac_content = generate_action(domains, socks_proxy, "socks5")
    with open("gfwpac.action", 'w') as f:
        f.write(pac_content)
    print('action file generated!')


# Press the green button in the gutter to run the script.
if __name__ == '__main__':
    print("import pacfile to privoxy action file.")
    main()

# See PyCharm help at https://www.jetbrains.com/help/pycharm/
