-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathgenpac.py
executable file
·110 lines (95 loc) · 4.3 KB
/
genpac.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python3
import argparse
import base64
import binascii
import json
import logging
import os
import re
import urllib.parse
import urllib.request
prog = 'GenPAC'
desc = 'generate PAC file from gfwlist and user-rules'
version = '0.1'
logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
level=logging.INFO)
default_gfwlist_url = 'https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt'
args_dict = {}
def update_args():
if urllib.parse.urlparse(args_dict['gfwlist']).scheme == '':
args_dict['gfwlist'] = 'file://' + \
os.path.abspath(args_dict['gfwlist'])
args_dict['proxy'] = args_dict['proxy'].strip()
if not args_dict['proxy'].endswith(';'):
args_dict['proxy'] += ';'
args_dict['gfwlist'] = urllib.parse.urlparse(args_dict['gfwlist'])
def get_gfwlist():
# req = urllib.request.Request(gfwlist_url)
try:
with urllib.request.urlopen(args_dict['gfwlist'].geturl()) as f:
base64_string = f.read().decode('utf-8').rstrip()
return base64_string
except (urllib.error.HTTPError, urllib.error.URLError) as e:
logging.error(
'Error {} occured when retrieving gfwlist from "{}"'.format(e, args_dict['gfwlist'].geturl()))
def generate_pac():
gfwlist_base64 = get_gfwlist()
if gfwlist_base64 == None:
return
try:
gfwlist = base64.b64decode(gfwlist_base64).decode('utf-8')
lines = gfwlist.split('\n')
if args_dict['user_rule'] != None:
userRuleString = args_dict['user_rule'].read()
userRuleLines = userRuleString.split('\n')
# ignore rules confict with user rules
lines = userRuleLines + list(filter(lambda line: line not in userRuleLines and
line.lstrip('@@') not in userRuleLines and
('@@' + line) not in userRuleLines, lines))
# filter empty and comment lines
lines = list(filter(lambda s: s.strip() !=
'' and not re.match(r'^[!\[].*', s.lstrip()), lines))
# rule lines to json array
ruleJsonString = json.dumps(lines, indent=2)
# get raw pac js
jsString = None
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'pac_template.js'), 'r') as f:
jsString = f.read()
jsString = jsString.replace('__RULES__', ruleJsonString)
jsString = jsString.replace('__PROXY__', args_dict['proxy'])
# write to output
with open(args_dict['output'], 'w') as f:
f.write(jsString)
return True
except binascii.Error as e:
logging.error('Error <{}> occured when decoding gfwlist'.format(e))
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog=prog, description=desc)
parser.add_argument('-v', '--version', action='version',
version='{} {}'.format(prog, version))
parser.add_argument('-g', '--gfwlist', type=str, required=False,
default=default_gfwlist_url,
help='a local path or remote url to the gfwlist encoded in base64')
parser.add_argument('-u', '--user-rule', type=argparse.FileType('r'),
required=False, metavar='/path/to/user-rule',
help='the file contains user rules')
parser.add_argument('--log-level', type=str, required=False, metavar='LEVEL',
default='INFO',
choices=['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'],
help='the value must be "DEBUG", "INFO" (default), "WARN", "ERROR" or "FATAL')
parser.add_argument('-p', '--proxy', type=str, required=True,
help='proxy string in PAC file')
parser.add_argument('output', type=str,
help='the path to the output PAC file')
args = parser.parse_args()
args_dict = vars(args)
update_args()
# if validate_args == False:
# exit()
logging.getLogger().setLevel(getattr(logging, args_dict['log_level']))
logging.debug('args: {}'.format(args_dict))
if generate_pac():
logging.info('Generate PAC: Succeeded')
else:
logging.warn('Generate PAC: Failed')