|
@ -10,167 +10,175 @@ import sys |
|
|
import json |
|
|
import json |
|
|
import base64 |
|
|
import base64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MuJsonLoader(object): |
|
|
class MuJsonLoader(object): |
|
|
def __init__(self): |
|
|
def __init__(self): |
|
|
self.json = None |
|
|
self.json = None |
|
|
|
|
|
|
|
|
|
|
|
def load(self, path): |
|
|
|
|
|
with open(path, 'rb+') as f: |
|
|
|
|
|
self.json = json.loads(f.read().decode('utf8')) |
|
|
|
|
|
|
|
|
def load(self, path): |
|
|
def save(self, path): |
|
|
with open(path, 'rb+') as f: |
|
|
if self.json: |
|
|
self.json = json.loads(f.read().decode('utf8')) |
|
|
output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': ')) |
|
|
|
|
|
with open(path, 'r+') as f: |
|
|
|
|
|
f.write(output) |
|
|
|
|
|
f.truncate() |
|
|
|
|
|
|
|
|
def save(self, path): |
|
|
|
|
|
if self.json: |
|
|
|
|
|
output = json.dumps(self.json, sort_keys=True, indent=4, separators=(',', ': ')) |
|
|
|
|
|
with open(path, 'r+') as f: |
|
|
|
|
|
f.write(output) |
|
|
|
|
|
f.truncate() |
|
|
|
|
|
|
|
|
|
|
|
class MuMgr(object): |
|
|
class MuMgr(object): |
|
|
def __init__(self): |
|
|
def __init__(self): |
|
|
self.config_path = get_config().MUDB_FILE |
|
|
self.config_path = get_config().MUDB_FILE |
|
|
try: |
|
|
try: |
|
|
self.server_addr = get_config().SERVER_PUB_ADDR |
|
|
self.server_addr = get_config().SERVER_PUB_ADDR |
|
|
except: |
|
|
except: |
|
|
self.server_addr = '127.0.0.1' |
|
|
self.server_addr = '127.0.0.1' |
|
|
self.data = MuJsonLoader() |
|
|
self.data = MuJsonLoader() |
|
|
|
|
|
|
|
|
|
|
|
if self.server_addr == '127.0.0.1': |
|
|
|
|
|
self.server_addr = self.getipaddr() |
|
|
|
|
|
|
|
|
if self.server_addr == '127.0.0.1': |
|
|
def getipaddr(self, ifname='eth0'): |
|
|
self.server_addr = self.getipaddr() |
|
|
import socket |
|
|
|
|
|
import struct |
|
|
|
|
|
import fcntl |
|
|
|
|
|
ret = '127.0.0.1' |
|
|
|
|
|
try: |
|
|
|
|
|
ret = socket.gethostbyname(socket.getfqdn(socket.gethostname())) |
|
|
|
|
|
except: |
|
|
|
|
|
pass |
|
|
|
|
|
if ret == '127.0.0.1': |
|
|
|
|
|
try: |
|
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|
|
|
|
|
ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24]) |
|
|
|
|
|
except: |
|
|
|
|
|
pass |
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
def getipaddr(self, ifname = 'eth0'): |
|
|
def ssrlink(self, user, encode): |
|
|
import socket |
|
|
protocol = user.get('protocol', '') |
|
|
import struct |
|
|
obfs = user.get('obfs', '') |
|
|
import fcntl |
|
|
protocol = protocol.replace("_compatible", "") |
|
|
ret = '127.0.0.1' |
|
|
obfs = obfs.replace("_compatible", "") |
|
|
try: |
|
|
link = "%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, |
|
|
ret = socket.gethostbyname(socket.getfqdn(socket.gethostname())) |
|
|
common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace( |
|
|
except: |
|
|
"=", "")) |
|
|
pass |
|
|
return "ssr://" + ( |
|
|
if ret == '127.0.0.1': |
|
|
encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link) |
|
|
try: |
|
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|
|
|
|
|
ret = socket.inet_ntoa(fcntl.ioctl(s.fileno(), 0x8915, struct.pack('256s', ifname[:15]))[20:24]) |
|
|
|
|
|
except: |
|
|
|
|
|
pass |
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
|
|
def ssrlink(self, user, encode): |
|
|
def userinfo(self, user): |
|
|
protocol = user.get('protocol', '') |
|
|
ret = "" |
|
|
obfs = user.get('obfs', '') |
|
|
for key in sorted(user): |
|
|
protocol = protocol.replace("_compatible", "") |
|
|
if key in ['enable']: |
|
|
obfs = obfs.replace("_compatible", "") |
|
|
continue |
|
|
link = "%s:%s:%s:%s:%s:%s" % (self.server_addr, user['port'], protocol, user['method'], obfs, common.to_str(base64.urlsafe_b64encode(common.to_bytes(user['passwd']))).replace("=", "")) |
|
|
ret += '\n' |
|
|
return "ssr://" + ( encode and common.to_str(base64.urlsafe_b64encode(common.to_bytes(link))).replace("=", "") or link) |
|
|
if key in ['transfer_enable', 'u', 'd']: |
|
|
|
|
|
val = user[key] |
|
|
|
|
|
if val / 1024 < 4: |
|
|
|
|
|
ret += " %s : %s" % (key, val) |
|
|
|
|
|
elif val / 1024 ** 2 < 4: |
|
|
|
|
|
val /= float(1024) |
|
|
|
|
|
ret += " %s : %s K Bytes" % (key, val) |
|
|
|
|
|
elif val / 1024 ** 3 < 4: |
|
|
|
|
|
val /= float(1024 ** 2) |
|
|
|
|
|
ret += " %s : %s M Bytes" % (key, val) |
|
|
|
|
|
else: |
|
|
|
|
|
val /= float(1024 ** 3) |
|
|
|
|
|
ret += " %s : %s G Bytes" % (key, val) |
|
|
|
|
|
else: |
|
|
|
|
|
ret += " %s : %s" % (key, user[key]) |
|
|
|
|
|
ret += "\n " + self.ssrlink(user, False) |
|
|
|
|
|
ret += "\n " + self.ssrlink(user, True) |
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
def userinfo(self, user): |
|
|
def rand_pass(self): |
|
|
ret = "" |
|
|
return ''.join( |
|
|
for key in sorted(user): |
|
|
[random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i |
|
|
if key in ['enable']: |
|
|
in range(8)]) |
|
|
continue |
|
|
|
|
|
ret += '\n' |
|
|
|
|
|
if key in ['transfer_enable', 'u', 'd'] : |
|
|
|
|
|
val = user[key] |
|
|
|
|
|
if val / 1024 < 4: |
|
|
|
|
|
ret += " %s : %s" % (key, val) |
|
|
|
|
|
elif val / 1024**2 < 4: |
|
|
|
|
|
val /= float(1024) |
|
|
|
|
|
ret += " %s : %s K Bytes" % (key, val) |
|
|
|
|
|
elif val / 1024**3 < 4: |
|
|
|
|
|
val /= float(1024**2) |
|
|
|
|
|
ret += " %s : %s M Bytes" % (key, val) |
|
|
|
|
|
else: |
|
|
|
|
|
val /= float(1024**3) |
|
|
|
|
|
ret += " %s : %s G Bytes" % (key, val) |
|
|
|
|
|
else: |
|
|
|
|
|
ret += " %s : %s" % (key, user[key]) |
|
|
|
|
|
ret += "\n " + self.ssrlink(user, False) |
|
|
|
|
|
ret += "\n " + self.ssrlink(user, True) |
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
|
|
def rand_pass(self): |
|
|
def add(self, user): |
|
|
return ''.join([random.choice('''ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~-_=+(){}[]^&%$@''') for i in range(8)]) |
|
|
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-cfb", |
|
|
|
|
|
'protocol': "auth_sha1_v4", |
|
|
|
|
|
'obfs': "tls1.2_ticket_auth_compatible", |
|
|
|
|
|
'transfer_enable': 1125899906842624} |
|
|
|
|
|
up['passwd'] = self.rand_pass() |
|
|
|
|
|
up.update(user) |
|
|
|
|
|
|
|
|
def add(self, user): |
|
|
self.data.load(self.config_path) |
|
|
up = {'enable': 1, 'u': 0, 'd': 0, 'method': "aes-128-cfb", |
|
|
for row in self.data.json: |
|
|
'protocol': "auth_sha1_v4", |
|
|
match = False |
|
|
'obfs': "tls1.2_ticket_auth_compatible", |
|
|
if 'user' in user and row['user'] == user['user']: |
|
|
'transfer_enable': 1125899906842624} |
|
|
match = True |
|
|
up['passwd'] = self.rand_pass() |
|
|
if 'port' in user and row['port'] == user['port']: |
|
|
up.update(user) |
|
|
match = True |
|
|
|
|
|
if match: |
|
|
|
|
|
print("user [%s] port [%s] already exist" % (row['user'], row['port'])) |
|
|
|
|
|
return |
|
|
|
|
|
self.data.json.append(up) |
|
|
|
|
|
print("### add user info %s" % self.userinfo(up)) |
|
|
|
|
|
self.data.save(self.config_path) |
|
|
|
|
|
|
|
|
self.data.load(self.config_path) |
|
|
def edit(self, user): |
|
|
for row in self.data.json: |
|
|
self.data.load(self.config_path) |
|
|
match = False |
|
|
for row in self.data.json: |
|
|
if 'user' in user and row['user'] == user['user']: |
|
|
match = True |
|
|
match = True |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
if 'port' in user and row['port'] == user['port']: |
|
|
match = False |
|
|
match = True |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
if match: |
|
|
match = False |
|
|
print("user [%s] port [%s] already exist" % (row['user'], row['port'])) |
|
|
if match: |
|
|
return |
|
|
print("edit user [%s]" % (row['user'],)) |
|
|
self.data.json.append(up) |
|
|
row.update(user) |
|
|
print("### add user info %s" % self.userinfo(up)) |
|
|
print("### new user info %s" % self.userinfo(row)) |
|
|
self.data.save(self.config_path) |
|
|
break |
|
|
|
|
|
self.data.save(self.config_path) |
|
|
|
|
|
|
|
|
def edit(self, user): |
|
|
def delete(self, user): |
|
|
self.data.load(self.config_path) |
|
|
self.data.load(self.config_path) |
|
|
for row in self.data.json: |
|
|
index = 0 |
|
|
match = True |
|
|
for row in self.data.json: |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
match = True |
|
|
match = False |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
match = False |
|
|
match = False |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
if match: |
|
|
match = False |
|
|
print("edit user [%s]" % (row['user'],)) |
|
|
if match: |
|
|
row.update(user) |
|
|
print("delete user [%s]" % row['user']) |
|
|
print("### new user info %s" % self.userinfo(row)) |
|
|
del self.data.json[index] |
|
|
break |
|
|
break |
|
|
self.data.save(self.config_path) |
|
|
index += 1 |
|
|
|
|
|
self.data.save(self.config_path) |
|
|
|
|
|
|
|
|
def delete(self, user): |
|
|
def clear_ud(self, user): |
|
|
self.data.load(self.config_path) |
|
|
up = {'u': 0, 'd': 0} |
|
|
index = 0 |
|
|
self.data.load(self.config_path) |
|
|
for row in self.data.json: |
|
|
for row in self.data.json: |
|
|
match = True |
|
|
match = True |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
match = False |
|
|
match = False |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
match = False |
|
|
match = False |
|
|
if match: |
|
|
if match: |
|
|
print("delete user [%s]" % row['user']) |
|
|
row.update(up) |
|
|
del self.data.json[index] |
|
|
print("clear user [%s]" % row['user']) |
|
|
break |
|
|
self.data.save(self.config_path) |
|
|
index += 1 |
|
|
|
|
|
self.data.save(self.config_path) |
|
|
|
|
|
|
|
|
|
|
|
def clear_ud(self, user): |
|
|
def list_user(self, user): |
|
|
up = {'u': 0, 'd': 0} |
|
|
self.data.load(self.config_path) |
|
|
self.data.load(self.config_path) |
|
|
if not user: |
|
|
for row in self.data.json: |
|
|
for row in self.data.json: |
|
|
match = True |
|
|
print("user [%s] port %s" % (row['user'], row['port'])) |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
return |
|
|
match = False |
|
|
for row in self.data.json: |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
match = True |
|
|
match = False |
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
if match: |
|
|
match = False |
|
|
row.update(up) |
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
print("clear user [%s]" % row['user']) |
|
|
match = False |
|
|
self.data.save(self.config_path) |
|
|
if match: |
|
|
|
|
|
print("### user [%s] info %s" % (row['user'], self.userinfo(row))) |
|
|
|
|
|
|
|
|
def list_user(self, user): |
|
|
|
|
|
self.data.load(self.config_path) |
|
|
|
|
|
if not user: |
|
|
|
|
|
for row in self.data.json: |
|
|
|
|
|
print("user [%s] port %s" % (row['user'], row['port'])) |
|
|
|
|
|
return |
|
|
|
|
|
for row in self.data.json: |
|
|
|
|
|
match = True |
|
|
|
|
|
if 'user' in user and row['user'] != user['user']: |
|
|
|
|
|
match = False |
|
|
|
|
|
if 'port' in user and row['port'] != user['port']: |
|
|
|
|
|
match = False |
|
|
|
|
|
if match: |
|
|
|
|
|
print("### user [%s] info %s" % (row['user'], self.userinfo(row))) |
|
|
|
|
|
|
|
|
|
|
|
def print_server_help(): |
|
|
def print_server_help(): |
|
|
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]... |
|
|
print('''usage: python mujson_manage.py -a|-d|-e|-c|-l [OPTION]... |
|
@ -198,119 +206,120 @@ General options: |
|
|
-h, --help show this help message and exit |
|
|
-h, --help show this help message and exit |
|
|
''') |
|
|
''') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(): |
|
|
def main(): |
|
|
shortopts = 'adeclu:p:k:O:o:G:g:m:t:f:h' |
|
|
shortopts = 'adeclu:p:k:O:o:G:g:m:t:f:h' |
|
|
longopts = ['help'] |
|
|
longopts = ['help'] |
|
|
action = None |
|
|
action = None |
|
|
user = {} |
|
|
user = {} |
|
|
fast_set_obfs = {'0': 'plain', |
|
|
fast_set_obfs = {'0': 'plain', |
|
|
'+1': 'http_simple_compatible', |
|
|
'+1': 'http_simple_compatible', |
|
|
'1': 'http_simple', |
|
|
'1': 'http_simple', |
|
|
'+2': 'tls1.2_ticket_auth_compatible', |
|
|
'+2': 'tls1.2_ticket_auth_compatible', |
|
|
'2': 'tls1.2_ticket_auth'} |
|
|
'2': 'tls1.2_ticket_auth'} |
|
|
fast_set_protocol = {'0': 'origin', |
|
|
fast_set_protocol = {'0': 'origin', |
|
|
'+1': 'verify_sha1_compatible', |
|
|
'+1': 'verify_sha1_compatible', |
|
|
'1': 'verify_sha1', |
|
|
'1': 'verify_sha1', |
|
|
'2': 'auth_sha1', |
|
|
'2': 'auth_sha1', |
|
|
'3': 'auth_sha1_v2', |
|
|
'3': 'auth_sha1_v2', |
|
|
'4': 'auth_sha1_v4', |
|
|
'4': 'auth_sha1_v4', |
|
|
'am': 'auth_aes128_md5', |
|
|
'am': 'auth_aes128_md5', |
|
|
'as': 'auth_aes128_sha1', |
|
|
'as': 'auth_aes128_sha1', |
|
|
} |
|
|
} |
|
|
fast_set_method = {'a0': 'aes-128-cfb', |
|
|
fast_set_method = {'a0': 'aes-128-cfb', |
|
|
'a1': 'aes-192-cfb', |
|
|
'a1': 'aes-192-cfb', |
|
|
'a2': 'aes-256-cfb', |
|
|
'a2': 'aes-256-cfb', |
|
|
'r': 'rc4-md5', |
|
|
'r': 'rc4-md5', |
|
|
'r6': 'rc4-md5-6', |
|
|
'r6': 'rc4-md5-6', |
|
|
'c': 'chacha20', |
|
|
'c': 'chacha20', |
|
|
'ci': 'chacha20-ietf', |
|
|
'ci': 'chacha20-ietf', |
|
|
's': 'salsa20', |
|
|
's': 'salsa20', |
|
|
'b': 'bf-cfb', |
|
|
'b': 'bf-cfb', |
|
|
'm0': 'camellia-128-cfb', |
|
|
'm0': 'camellia-128-cfb', |
|
|
'm1': 'camellia-192-cfb', |
|
|
'm1': 'camellia-192-cfb', |
|
|
'm2': 'camellia-256-cfb', |
|
|
'm2': 'camellia-256-cfb', |
|
|
'a0t': 'aes-128-ctr', |
|
|
'a0t': 'aes-128-ctr', |
|
|
'a1t': 'aes-192-ctr', |
|
|
'a1t': 'aes-192-ctr', |
|
|
'a2t': 'aes-256-ctr'} |
|
|
'a2t': 'aes-256-ctr'} |
|
|
try: |
|
|
try: |
|
|
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) |
|
|
optlist, args = getopt.getopt(sys.argv[1:], shortopts, longopts) |
|
|
for key, value in optlist: |
|
|
for key, value in optlist: |
|
|
if key == '-a': |
|
|
if key == '-a': |
|
|
action = 1 |
|
|
action = 1 |
|
|
elif key == '-d': |
|
|
elif key == '-d': |
|
|
action = 2 |
|
|
action = 2 |
|
|
elif key == '-e': |
|
|
elif key == '-e': |
|
|
action = 3 |
|
|
action = 3 |
|
|
elif key == '-l': |
|
|
elif key == '-l': |
|
|
action = 4 |
|
|
action = 4 |
|
|
elif key == '-c': |
|
|
elif key == '-c': |
|
|
action = 0 |
|
|
action = 0 |
|
|
elif key == '-u': |
|
|
elif key == '-u': |
|
|
user['user'] = value |
|
|
user['user'] = value |
|
|
elif key == '-p': |
|
|
elif key == '-p': |
|
|
user['port'] = int(value) |
|
|
user['port'] = int(value) |
|
|
elif key == '-k': |
|
|
elif key == '-k': |
|
|
user['passwd'] = value |
|
|
user['passwd'] = value |
|
|
elif key == '-o': |
|
|
elif key == '-o': |
|
|
if value in fast_set_obfs: |
|
|
if value in fast_set_obfs: |
|
|
user['obfs'] = fast_set_obfs[value] |
|
|
user['obfs'] = fast_set_obfs[value] |
|
|
else: |
|
|
else: |
|
|
user['obfs'] = value |
|
|
user['obfs'] = value |
|
|
elif key == '-O': |
|
|
elif key == '-O': |
|
|
if value in fast_set_protocol: |
|
|
if value in fast_set_protocol: |
|
|
user['protocol'] = fast_set_protocol[value] |
|
|
user['protocol'] = fast_set_protocol[value] |
|
|
else: |
|
|
else: |
|
|
user['protocol'] = value |
|
|
user['protocol'] = value |
|
|
elif key == '-g': |
|
|
elif key == '-g': |
|
|
user['obfs_param'] = value |
|
|
user['obfs_param'] = value |
|
|
elif key == '-G': |
|
|
elif key == '-G': |
|
|
user['protocol_param'] = value |
|
|
user['protocol_param'] = value |
|
|
elif key == '-m': |
|
|
elif key == '-m': |
|
|
if value in fast_set_method: |
|
|
if value in fast_set_method: |
|
|
user['method'] = fast_set_method[value] |
|
|
user['method'] = fast_set_method[value] |
|
|
else: |
|
|
else: |
|
|
user['method'] = value |
|
|
user['method'] = value |
|
|
elif key == '-f': |
|
|
elif key == '-f': |
|
|
user['forbidden_port'] = value |
|
|
user['forbidden_port'] = value |
|
|
elif key == '-t': |
|
|
elif key == '-t': |
|
|
val = float(value) |
|
|
val = float(value) |
|
|
try: |
|
|
try: |
|
|
val = int(value) |
|
|
val = int(value) |
|
|
except: |
|
|
except: |
|
|
pass |
|
|
pass |
|
|
user['transfer_enable'] = val * (1024 ** 3) |
|
|
user['transfer_enable'] = val * (1024 ** 3) |
|
|
elif key in ('-h', '--help'): |
|
|
elif key in ('-h', '--help'): |
|
|
print_server_help() |
|
|
print_server_help() |
|
|
sys.exit(0) |
|
|
sys.exit(0) |
|
|
except getopt.GetoptError as e: |
|
|
except getopt.GetoptError as e: |
|
|
print(e) |
|
|
print(e) |
|
|
sys.exit(2) |
|
|
sys.exit(2) |
|
|
|
|
|
|
|
|
manage = MuMgr() |
|
|
manage = MuMgr() |
|
|
if action == 0: |
|
|
if action == 0: |
|
|
manage.clear_ud(user) |
|
|
manage.clear_ud(user) |
|
|
elif action == 1: |
|
|
elif action == 1: |
|
|
if 'user' not in user and 'port' in user: |
|
|
if 'user' not in user and 'port' in user: |
|
|
user['user'] = str(user['port']) |
|
|
user['user'] = str(user['port']) |
|
|
if 'user' in user and 'port' in user: |
|
|
if 'user' in user and 'port' in user: |
|
|
manage.add(user) |
|
|
manage.add(user) |
|
|
else: |
|
|
else: |
|
|
print("You have to set the port with -p") |
|
|
print("You have to set the port with -p") |
|
|
elif action == 2: |
|
|
elif action == 2: |
|
|
if 'user' in user or 'port' in user: |
|
|
if 'user' in user or 'port' in user: |
|
|
manage.delete(user) |
|
|
manage.delete(user) |
|
|
else: |
|
|
else: |
|
|
print("You have to set the user name or port with -u/-p") |
|
|
print("You have to set the user name or port with -u/-p") |
|
|
elif action == 3: |
|
|
elif action == 3: |
|
|
if 'user' in user or 'port' in user: |
|
|
if 'user' in user or 'port' in user: |
|
|
manage.edit(user) |
|
|
manage.edit(user) |
|
|
else: |
|
|
else: |
|
|
print("You have to set the user name or port with -u/-p") |
|
|
print("You have to set the user name or port with -u/-p") |
|
|
elif action == 4: |
|
|
elif action == 4: |
|
|
manage.list_user(user) |
|
|
manage.list_user(user) |
|
|
elif action is None: |
|
|
elif action is None: |
|
|
print_server_help() |
|
|
print_server_help() |
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
main() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
main() |
|
|