#!/usr/bin/python -tt
#
# Copyright (c) 2010, 2011 Intel, Inc.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; version 2 of the License
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc., 59
# Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import os
import re
import urlparse
from mic import msger

_my_proxies = {}
_my_noproxy = None
_my_noproxy_list = []

def set_proxy_environ():
    if not _my_proxies:
        return
    for key in _my_proxies.keys():
        os.environ[key + "_proxy"] = _my_proxies[key]
    if not _my_noproxy:
        return
    os.environ["no_proxy"] = _my_noproxy

def unset_proxy_environ():
    for env in ('http_proxy',
                'https_proxy',
                'ftp_proxy',
                'all_proxy'):
        if env in os.environ:
            del os.environ[env]

        env_upper = env.upper()
        if env_upper in os.environ:
            del os.environ[env_upper]

def _set_proxies(proxy = None, no_proxy = None):
    """Return a dictionary of scheme -> proxy server URL mappings.
    """

    global _my_noproxy, _my_proxies
    _my_proxies = {}
    _my_noproxy = None
    proxies = []
    if proxy:
        proxies.append(("http_proxy", proxy))
    if no_proxy:
        proxies.append(("no_proxy", no_proxy))

    # Get proxy settings from environment if not provided
    if not proxy and not no_proxy:
        proxies = os.environ.items()

        # Remove proxy env variables, urllib2 can't handle them correctly
        unset_proxy_environ()

    for name, value in proxies:
        name = name.lower()
        if value and name[-6:] == '_proxy':
            if name[0:2] != "no":
                _my_proxies[name[:-6]] = value
            else:
                _my_noproxy = value

def _ip_to_int(ip):
    ipint = 0
    shift = 24
    for dec in ip.split("."):
        if not dec.isdigit():
            continue
        ipint |= int(dec) << shift
        shift -= 8
    return ipint

def _int_to_ip(val):
    ipaddr = ""
    shift = 0
    for i in range(4):
        dec = val >> shift
        dec &= 0xff
        ipaddr = ".%d%s" % (dec, ipaddr)
        shift += 8
    return ipaddr[1:]

def _isip(host):
    if host.replace(".", "").isdigit():
        return True
    return False

def _set_noproxy_list():
    global _my_noproxy, _my_noproxy_list
    _my_noproxy_list = []
    if not _my_noproxy:
        return

    #solve in /etc/enviroment contains command like `echo 165.xxx.xxx.{1..255} | sed 's/ /,/g'``
    _my_noproxy_bak = _my_noproxy
    start = _my_noproxy.find("`")
    while(start < len(_my_noproxy) and start != -1):
        start = _my_noproxy.find("`",start)
        end = _my_noproxy.find("`",start+1)
        cmd = _my_noproxy[start+1:end]
        pstr = _my_noproxy[start:end+1]
        start = end + 1

        _my_noproxy=_my_noproxy.replace(pstr,len(pstr)*" ")
        try:
            c_result = os.popen(cmd).readlines()
            if len(c_result) == 0:
                continue
        except Exception as e:
            msger.warning(str(e))
            continue
        to_list = c_result[0].strip("\n").split(",")
        _my_noproxy_list.extend(to_list)

    for item in _my_noproxy.split(","):
        item = item.strip()
        if not item:
            continue

        if item[0] != '.' and item.find("/") == -1:
            # Need to match it
            _my_noproxy_list.append({"match":0, "needle":item})

        elif item[0] == '.':
            # Need to match at tail
            _my_noproxy_list.append({"match":1, "needle":item})

        elif item.find("/") > 3:
            # IP/MASK, need to match at head
            needle = item[0:item.find("/")].strip()
            ip = _ip_to_int(needle)
            netmask = 0
            mask = item[item.find("/")+1:].strip()

            if mask.isdigit():
                netmask = int(mask)
                netmask = ~((1<<(32-netmask)) - 1)
                ip &= netmask
            else:
                shift = 24
                netmask = 0
                for dec in mask.split("."):
                    if not dec.isdigit():
                        continue
                    netmask |= int(dec) << shift
                    shift -= 8
                ip &= netmask

            _my_noproxy_list.append({"match":2, "needle":ip, "netmask":netmask})
    _my_noproxy = _my_noproxy_bak

def _isnoproxy(url):
    host = urlparse.urlparse(url)[1]
    # urlparse.urlparse(url) returns (scheme, host, path, parm, query, frag)

    if '@' in host:
        user_pass, host = host.split('@', 1)

    if ':' in host:
        host, port = host.split(':', 1)

    hostisip = _isip(host)
    for item in _my_noproxy_list:
        if hostisip and item["match"] == 1:
            continue

        if item["match"] == 2 and hostisip:
            if (_ip_to_int(host) & item["netmask"]) == item["needle"]:
                return True

        if item["match"] == 0:
            if host == item["needle"]:
                return True

        if item["match"] == 1:
            if re.match(r".*%s$" % item["needle"], host):
                return True

    return False

def set_proxies(proxy = None, no_proxy = None):
    _set_proxies(proxy, no_proxy)
    _set_noproxy_list()
    set_proxy_environ()

def get_proxy_for(url):
    if url.startswith('file:') or _isnoproxy(url):
        return None

    type = url[0:url.index(":")]
    proxy = None
    if _my_proxies.has_key(type):
        proxy = _my_proxies[type]
    elif _my_proxies.has_key("http"):
        proxy = _my_proxies["http"]
    else:
        proxy = None

    return proxy
