-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfinal_shell_decoder_cli.py
More file actions
213 lines (170 loc) · 7.3 KB
/
Copy pathfinal_shell_decoder_cli.py
File metadata and controls
213 lines (170 loc) · 7.3 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#!/usr/bin/env python3
"""FinalShell 配置解析工具 — 命令行版本."""
from __future__ import annotations
import argparse
import logging
import sys
from pathlib import Path
from src.core import (
ServerCredential,
detect_final_shell_path,
is_final_shell_dir,
load_credentials,
load_key_data_map,
)
__version__ = "1.0.0"
logger = logging.getLogger("fsdp")
def _setup_logging(verbose: bool) -> None:
"""Configure the root logger for CLI output."""
level = logging.DEBUG if verbose else logging.INFO
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter("%(message)s"))
logger.setLevel(level)
logger.addHandler(handler)
logger.propagate = False
# ---- output helpers --------------------------------------------------------
def print_credentials(creds: list[ServerCredential]) -> None:
"""Print parsed credentials to stdout."""
if not creds:
logger.info("没有找到任何连接配置")
return
logger.info("\n共找到 %d 条服务器记录:", len(creds))
logger.info("=" * 80)
for i, cred in enumerate(creds, 1):
name = cred.name or cred.file_name
port = str(cred.port) if cred.port > 0 else ""
logger.info("")
logger.info("[%d] %s", i, name)
logger.info(" 主机: %s:%s", cred.host, port)
logger.info(" 用户名: %s", cred.user_name)
logger.info(" 认证方式: %s", cred.auth_mode)
if cred.encrypted_password:
logger.info(" 密码: %s", cred.decoded_password or "[解密失败]")
if cred.decoded_private_key and not cred.decoded_private_key.startswith("["):
logger.info(" 私钥:")
logger.info(cred.decoded_private_key)
logger.info("")
logger.info("=" * 80)
def save_private_keys(creds: list[ServerCredential], output_dir: str) -> int:
"""Save decoded private keys to *output_dir*. Returns count of saved files."""
out = Path(output_dir)
out.mkdir(exist_ok=True)
saved = 0
for cred in creds:
if cred.decoded_private_key and not cred.decoded_private_key.startswith("["):
key_name = (cred.name or cred.host).replace(" ", "_") + "_private_key"
key_path = out / key_name
try:
# Remove all extra whitespace and ensure proper format
lines = [line.strip() for line in cred.decoded_private_key.split('\n') if line.strip()]
cleaned_key = '\n'.join(lines) + '\n'
key_path.write_text(cleaned_key, encoding="utf-8")
logger.info(" 已保存私钥: %s", key_path)
saved += 1
except OSError as exc:
logger.error(" 保存私钥失败 %s: %s", key_name, exc)
return saved
# ---- argument parsing ------------------------------------------------------
def _parse_args(argv: list[str] | None = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="fsdp-cli",
description="FinalShell 配置解析工具 — 命令行版本",
)
parser.add_argument(
"directory",
nargs="?",
help="FinalShell 安装目录(默认为自动检测)",
)
parser.add_argument(
"--version", "-V",
action="version",
version=f"%(prog)s {__version__}",
)
parser.add_argument(
"--verbose", "-v",
action="store_true",
help="显示调试信息",
)
return parser.parse_args(argv)
# ---- main ------------------------------------------------------------------
def main(argv: list[str] | None = None) -> int:
args = _parse_args(argv)
_setup_logging(args.verbose)
logger.info("FinalShell 配置解析工具 (命令行版)")
logger.info("=" * 50)
# 1. Resolve path
base_dir: Path | None = None
if args.directory:
base_dir = Path(args.directory)
if not is_final_shell_dir(base_dir):
logger.error("错误: 目录 '%s' 不是有效的 FinalShell 目录(未找到 conn 文件夹)", base_dir)
return 1
else:
base_dir = detect_final_shell_path()
if base_dir is None:
path = input("请输入 FinalShell 安装目录路径: ").strip()
if not path:
logger.error("错误: 路径不能为空")
return 1
base_dir = Path(path)
if not is_final_shell_dir(base_dir):
logger.error("错误: 目录 '%s' 不是有效的 FinalShell 目录", base_dir)
return 1
else:
logger.info("已自动检测到 FinalShell 目录: %s", base_dir)
# 2. Load and parse
conn_dir = base_dir / "conn"
config_path = base_dir / "config.json"
logger.info("正在加载私钥数据...")
key_data_map = load_key_data_map(config_path)
logger.info("正在解析 %s 目录下的配置文件...", conn_dir)
creds = load_credentials(conn_dir, key_data_map)
logger.info("解析完成: 共找到 %d 条服务器记录", len(creds))
if not config_path.exists():
logger.warning("未找到 config.json,私钥无法解析")
# 3. Output
print_credentials(creds)
# 4. Optional private-key export
# Filter credentials with valid private keys
creds_with_keys = [
cred for cred in creds
if cred.decoded_private_key and not cred.decoded_private_key.startswith("[")
]
if creds_with_keys:
logger.info("\n检测到 %d 个有效的私钥:", len(creds_with_keys))
for i, cred in enumerate(creds_with_keys, 1):
name = cred.name or cred.host
logger.info(" [%d] %s", i, name)
choice = input("\n是否保存私钥到文件? (y/n): ").strip().lower()
if choice in ("y", "yes"):
# If only one key, save directly without prompt
if len(creds_with_keys) == 1:
selected_creds = [creds_with_keys[0]]
logger.info("\n自动选择唯一的私钥")
else:
# Prompt for which keys to save when multiple keys exist
while True:
key_choice = input("请选择要保存的私钥编号 (0=全部, 1-%d): " % len(creds_with_keys)).strip()
if not key_choice:
continue
if key_choice == "0":
# Save all keys
selected_creds = creds_with_keys
break
else:
try:
# Save specific key
idx = int(key_choice)
if 1 <= idx <= len(creds_with_keys):
selected_creds = [creds_with_keys[idx - 1]]
break
else:
logger.warning("无效的编号,请输入 0-%d", len(creds_with_keys))
except ValueError:
logger.warning("请输入有效的数字")
out_dir = input("请输入保存路径 (默认为当前目录 output/): ").strip() or "output"
saved = save_private_keys(selected_creds, out_dir)
logger.info("已保存 %d 个私钥文件", saved)
return 0
if __name__ == "__main__":
sys.exit(main())