diff --git a/auth-sample b/auth-sample index 737f0b3..92aaec9 100644 --- a/auth-sample +++ b/auth-sample @@ -1,3 +1,4 @@ -2020111222 -Bistu111222 -192.168.211.3 +[DEFAULT] +username = 2020000000 +password = Bistu000000 +host = 192.168.211.3 diff --git a/bistu_wifi/__init__.py b/bistu_wifi/__init__.py new file mode 100644 index 0000000..79382cc --- /dev/null +++ b/bistu_wifi/__init__.py @@ -0,0 +1,2 @@ +from .bistu_wifi import login +from .bistu_wifi import logout diff --git a/bistu_wifi/__main__.py b/bistu_wifi/__main__.py new file mode 100644 index 0000000..898a053 --- /dev/null +++ b/bistu_wifi/__main__.py @@ -0,0 +1,86 @@ +#!/usr/bin/python3 + +import os +import logging +import configparser +import argparse +from bistu_wifi.bistu_wifi import logger +from bistu_wifi.bistu_wifi import login +from bistu_wifi.bistu_wifi import logout + + +if 'XDG_CONFIG_HOME' in os.environ: + XDG_CONFIG_HOME = os.environ['XDG_CONFIG_HOME'] +else: + XDG_CONFIG_HOME = os.path.join(os.environ['HOME'], '.config') + +DEFAULT_HOST = '10.1.206.13' +DEFAULT_CONFIG_LOCATION = os.path.join(XDG_CONFIG_HOME, 'bistu-wifi/auth.ini') + + +def main(): + + parser = argparse.ArgumentParser() + parser.description = 'login or logout bistu wifi' + + parser.add_argument('-u', '--username', nargs='?', + type=str, help='username you use to login bistu wifi') + parser.add_argument('-p', '--password', nargs='?', + type=str, help='password you use to login bistu wifi') + parser.add_argument('--host', nargs='?', default=None, + type=str, help='host to send login/logout request to, default use 10.1.206.13') + parser.add_argument('-o', '--logout', action='store_true', + help='logout bistu wifi. try to login if not privided') + parser.add_argument('-c', '--config', nargs='?', default=DEFAULT_CONFIG_LOCATION, + type=str, help='load info(username, password, host) from config file') + parser.add_argument('--notime', action='store_true', + help='don\'t output time field in log') + + args = parser.parse_args() + + log_level = logging.DEBUG if 'BISTU_WIFI_DEBUG' in os.environ else logging.INFO + log_fmt = '%(levelname)s : %(funcName)s : %(message)s' + if not args.notime: + log_fmt = '%(asctime)s : ' + log_fmt + + logging.basicConfig(level=log_level, format=log_fmt) + + username = None + password = None + host = DEFAULT_HOST + is_logout = False + + if args.config is not None: + logger.info('loading info from config file %s ...', args.config) + + config = configparser.ConfigParser() + config.read(args.config) + + d_section = config['DEFAULT'] + + username = str(d_section['username']) if 'username' in d_section else username + password = str(d_section['password']) if 'password' in d_section else password + host = str(d_section['host']) if 'host' in d_section else host + + username = args.username if args.username is not None else username + password = args.password if args.password is not None else password + host = args.host if args.host is not None else host + + is_logout = args.logout + + logger.info('parameter status: username = %s', username) + logger.info('parameter status: password = %s', None if password is None else '*' * len(password)) + logger.info('parameter status: host = %s', host) + logger.info('parameter status: logout = %s', is_logout) + + if is_logout: + logout(host) + else: + if username is None or password is None: + logger.error('cannot login wifi without username or password') + else: + login(username, password, host) + + +if __name__ == '__main__': + main() diff --git a/bistu_wifi/bistu_wifi.py b/bistu_wifi/bistu_wifi.py new file mode 100644 index 0000000..7879c18 --- /dev/null +++ b/bistu_wifi/bistu_wifi.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 + +import urllib.parse +import urllib.request +import urllib.response +import logging +import re + + +logger = logging.getLogger('bistu-wifi') + + +def generate_post_data(username: str, password: str): + ''' + Generate bytes to send to through HTTP POST, an example from browser: + + DDDDD=2021020000&upass=Bistu000000&R1=0&R2=&R3=0&R6=0¶=00&0MKKey=123456&buttonClicked=&redirect_url=&err_flag=&username=&password=&user=&cmd=&Login= + ''' + + data_template = { + 'DDDDD': None, + 'upass': None, + 'R1': 0, + 'R2': '', + 'R3': 0, + 'R6': 0, + 'para': 00, + '0MKKey': 123456, + 'buttonClicked': '', + 'redirect_url': '', + 'err_flag': '', + 'username': '', + 'password': '', + 'user': '', + 'cmd': '', + 'Login': '' + } + + data_template['DDDDD'] = username + data_template['upass'] = password + + # login_manager.generate_post_data_hook(data_template) + + for k, v in data_template.items(): + logger.debug('the generated post data: %s: %s', k, v) + + return bytes(urllib.parse.urlencode(data_template), 'utf-8') + + +def login(username: str, password: str, host: str): + ''' + There are some other host, the known are: + + - 10.1.206.3 (default) + - 192.168.211.3 + ''' + + login_url = f'http://{host}/a70.htm' + + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', + 'Accept-Language': 'en-US,zh-CN;q=0.5', + 'Accept-Encoding': 'gzip, deflate' + } + + post_data = generate_post_data(username, password) + + logger.debug('the dest url is: %s', login_url) + for k, v in headers.items(): + logger.debug('the headers: %s: %s', k, v) + logger.debug('the encoded post data: %s', str(post_data)) + + req = urllib.request.Request(url=login_url, data=post_data, headers=headers, method='POST') + + logger.info('sending the login request...') + + with urllib.request.urlopen(req) as response: + response_body = response.read().decode('gbk') + + html_title = re.search('(.*)', response_body)[1] + + logger.info('http_status: %d', response.status) + logger.info('title_of_response_html: %s', html_title) + logger.info('login_state: %s', 'success' if html_title == '认证成功页' else 'fail') + + +def logout(host: str): + ''' + param host: see login host + ''' + + logout_url = f'http://{host}/F.htm' + req = urllib.request.Request(url=logout_url) + + logger.info('sending the logout request...') + + with urllib.request.urlopen(req) as response: + response_body = response.read().decode('gbk') + + html_title = re.search('(.*)', response_body)[1] + + logger.info('http_status: %d', response.status) + logger.info('title_of_response_html: %s', html_title) + logger.info('logout_state: %s', 'success' if html_title == '信息页' else 'fail') + +#@staticmethod +#def generate_post_data_hook(post_data: dict) -> None: +# username = post_data['DDDDD'] +# username += '@bistu' +# post_data['DDDDD'] = username + diff --git a/main.py b/main.py deleted file mode 100644 index e9b8c45..0000000 --- a/main.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/usr/bin/python3 - -import urllib.parse -import urllib.request -import urllib.response -import logging -import re -import argparse - -logger = logging.getLogger('login_manager') - -def generate_post_data(username: str, password: str): - ''' - Generate bytes to send to through HTTP POST, an example from browser: - - DDDDD=2021020000&upass=Bistu000000&R1=0&R2=&R3=0&R6=0¶=00&0MKKey=123456&buttonClicked=&redirect_url=&err_flag=&username=&password=&user=&cmd=&Login= - ''' - - data_template = { - 'DDDDD': None, - 'upass': None, - 'R1': 0, - 'R2': '', - 'R3': 0, - 'R6': 0, - 'para': 00, - '0MKKey': 123456, - 'buttonClicked': '', - 'redirect_url': '', - 'err_flag': '', - 'username': '', - 'password': '', - 'user': '', - 'cmd': '', - 'Login': '' - } - - data_template['DDDDD'] = username - data_template['upass'] = password - - # login_manager.generate_post_data_hook(data_template) - - logger.debug('the generated post data is %s', str(data_template)) - - return bytes(urllib.parse.urlencode(data_template), 'utf-8') - -def login(username: str, password: str, host: str = '10.1.206.13'): - ''' - There are some other host, the known are: - - - 10.1.206.3 (default) - - 192.168.211.3 - ''' - headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8', - 'Accept-Language': 'en-US,zh-CN;q=0.5', - 'Accept-Encoding': 'gzip, deflate' - } - post_data = generate_post_data(username, password) - - login_url = f'http://{host}/a70.htm' - logger.debug('the dest url is: %s', login_url) - logger.debug('the headers is: %s', str(headers)) - logger.debug('the encoded post data is %s', str(post_data)) - - req = urllib.request.Request(url=login_url, data=post_data, headers=headers, method='POST') - - logger.info('sending the login request...') - - with urllib.request.urlopen(req) as response: - response_body = response.read().decode('gbk') - - html_title = re.search('(.*)', response_body)[1] - - logger.info('http_status: %d', response.status) - logger.info('title_of_response_html: %s', html_title) - logger.info('login_state: %s', 'success' if html_title == '认证成功页' else 'fail') - -def logout(host: str = '10.1.206.13'): - ''' - param host: see login host - ''' - - logout_url = f'http://{host}/F.htm' - req = urllib.request.Request(url=logout_url) - - logger.info('sending the logout request...') - - with urllib.request.urlopen(req) as response: - response_body = response.read().decode('gbk') - - html_title = re.search('(.*)', response_body)[1] - - logger.info('http_status: %d', response.status) - logger.info('title_of_response_html: %s', html_title) - logger.info('logout_state: %s', 'success' if html_title == '信息页' else 'fail') - - #@staticmethod - #def generate_post_data_hook(post_data: dict) -> None: - # username = post_data['DDDDD'] - # username += '@bistu' - # post_data['DDDDD'] = username - - -def load_auth_info(path: str): - with open(path, 'r', encoding='utf-8') as file: - username = file.readline().strip() - password = file.readline().strip() - - host = file.readline().strip() - if host == '': - host = None - - return username, password, host - -def main(): - logging.basicConfig(level=logging.DEBUG) - logger = logging.getLogger('main') - - parser = argparse.ArgumentParser() - parser.description = 'login or logout bistu wifi' - parser.epilog = 'the config file format(without quote): "{username}\\n{password}". The "\\n" is a newline charactor' - - parser.add_argument('-u', '--username', nargs='?', - type=str, help='username you use to login bistu wifi, logout if not provided') - parser.add_argument('-p', '--password', nargs='?', - type=str, help='password you use to login bistu wifi, logout if not provided') - parser.add_argument('--host', nargs='?', default=None, - type=str, help='host to send login/logout request to, default use 10.1.206.13') - parser.add_argument('-o', '--logout', nargs='?', type=bool, default=False, - help='logout bistu wifi') - parser.add_argument('-c', '--config', nargs='?', type=str, - help='load info(username, password, host) from config file') - - args = parser.parse_args() - - logger.debug('parse argument: username= %s', args.username) - logger.debug('parse argument: password= %s', args.password) - logger.debug('parse argument: host= %s', args.host) - logger.debug('parse argument: logout= %s', args.logout) - - if args.config is not None: - username, password, host = load_auth_info(args.config) - - if host is None: - login(username, password) - else: - login(username, password, host) - - elif args.username is not None and args.password is not None: - if args.host is None: - login(args.username, args.password) - else: - login(args.username, args.password, args.host) - - elif args.logout: - if args.host is None: - logout() - else: - logout(args.host) - else: - parser.print_help() - - -if __name__ == '__main__': - main() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d2aa7f5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[project] +name = "bistu_wifi" +version = "0.0.1" +dependencies = [] +[project.scripts] +bistu-wifi = "bistu_wifi.__main__:main"