InPanel 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. inpanel/__init__.py +29 -0
  2. inpanel/acme.py +289 -0
  3. inpanel/aliyuncs.py +269 -0
  4. inpanel/app.py +514 -0
  5. inpanel/apt.py +223 -0
  6. inpanel/base.py +200 -0
  7. inpanel/config.py +139 -0
  8. inpanel/data/config.ini +29 -0
  9. inpanel/data/history +5 -0
  10. inpanel/data/runlogs.ini +12 -0
  11. inpanel/data/update_info.ini +4 -0
  12. inpanel/dnf.py +225 -0
  13. inpanel/lib/__init__.py +9 -0
  14. inpanel/lib/async_process.py +98 -0
  15. inpanel/lib/filelock.py +82 -0
  16. inpanel/lib/magic.py +233 -0
  17. inpanel/lib/pxssh.py +310 -0
  18. inpanel/lib/pyDes.py +867 -0
  19. inpanel/mod/__init__.py +0 -0
  20. inpanel/mod/apt.py +316 -0
  21. inpanel/mod/certificate.py +268 -0
  22. inpanel/mod/config.py +372 -0
  23. inpanel/mod/cron.py +315 -0
  24. inpanel/mod/disk.py +322 -0
  25. inpanel/mod/dnf.py +239 -0
  26. inpanel/mod/file.py +722 -0
  27. inpanel/mod/firewall.py +199 -0
  28. inpanel/mod/firewall_base.py +137 -0
  29. inpanel/mod/firewall_firewalld.py +170 -0
  30. inpanel/mod/firewall_iptables.py +176 -0
  31. inpanel/mod/firewall_macos.py +213 -0
  32. inpanel/mod/firewall_ufw.py +128 -0
  33. inpanel/mod/ftp.py +231 -0
  34. inpanel/mod/httpd.py +1205 -0
  35. inpanel/mod/lighttpd.py +26 -0
  36. inpanel/mod/login.py +98 -0
  37. inpanel/mod/mysql.py +577 -0
  38. inpanel/mod/named.py +26 -0
  39. inpanel/mod/nginx.py +2327 -0
  40. inpanel/mod/package.py +33 -0
  41. inpanel/mod/package_apt.py +62 -0
  42. inpanel/mod/package_base.py +77 -0
  43. inpanel/mod/package_dnf.py +62 -0
  44. inpanel/mod/package_map.py +366 -0
  45. inpanel/mod/package_yum.py +64 -0
  46. inpanel/mod/php.py +289 -0
  47. inpanel/mod/process.py +291 -0
  48. inpanel/mod/proftpd.py +26 -0
  49. inpanel/mod/pureftpd.py +26 -0
  50. inpanel/mod/query.py +108 -0
  51. inpanel/mod/server.py +1319 -0
  52. inpanel/mod/service.py +262 -0
  53. inpanel/mod/setting.py +228 -0
  54. inpanel/mod/shell.py +78 -0
  55. inpanel/mod/ssh.py +280 -0
  56. inpanel/mod/system.py +220 -0
  57. inpanel/mod/task.py +1337 -0
  58. inpanel/mod/ufw.py +14 -0
  59. inpanel/mod/user.py +306 -0
  60. inpanel/mod/vsftpd.py +67 -0
  61. inpanel/mod/yum.py +243 -0
  62. inpanel/plugins/__init__.py +10 -0
  63. inpanel/plugins/__pycache__/__init__.cpython-313.pyc +0 -0
  64. inpanel/plugins/acme/static/index.html +10 -0
  65. inpanel/plugins/acme/static/index.js +13 -0
  66. inpanel/plugins/shadowsocks-libev/__pycache__/info.cpython-313.pyc +0 -0
  67. inpanel/plugins/shadowsocks-libev/info.py +73 -0
  68. inpanel/plugins/shadowsocks-libev/static/index.html +70 -0
  69. inpanel/plugins/shadowsocks-libev/static/index.js +18 -0
  70. inpanel/public/css/global.css +138 -0
  71. inpanel/public/favicon.ico +0 -0
  72. inpanel/public/images/alipay.jpg +0 -0
  73. inpanel/public/images/loading.gif +0 -0
  74. inpanel/public/images/weixin.jpg +0 -0
  75. inpanel/public/index.html +94 -0
  76. inpanel/public/js/controllers/controllers.js +566 -0
  77. inpanel/public/js/controllers/database.js +590 -0
  78. inpanel/public/js/controllers/ecs.js +624 -0
  79. inpanel/public/js/controllers/file.js +940 -0
  80. inpanel/public/js/controllers/plugins.js +105 -0
  81. inpanel/public/js/controllers/service.js +1460 -0
  82. inpanel/public/js/controllers/site.js +1241 -0
  83. inpanel/public/js/controllers/utils.js +2319 -0
  84. inpanel/public/js/core.js +221 -0
  85. inpanel/public/js/directives.js +784 -0
  86. inpanel/public/js/filters.js +395 -0
  87. inpanel/public/js/services.js +257 -0
  88. inpanel/public/lib/angular/1.0.2/angular-resource.min.js +10 -0
  89. inpanel/public/lib/angular/1.0.2/angular.min.js +158 -0
  90. inpanel/public/lib/bootstrap/css/bootstrap-theme.css +587 -0
  91. inpanel/public/lib/bootstrap/css/bootstrap-theme.css.map +1 -0
  92. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css +6 -0
  93. inpanel/public/lib/bootstrap/css/bootstrap-theme.min.css.map +1 -0
  94. inpanel/public/lib/bootstrap/css/bootstrap.css +6757 -0
  95. inpanel/public/lib/bootstrap/css/bootstrap.css.map +1 -0
  96. inpanel/public/lib/bootstrap/css/bootstrap.min.css +6 -0
  97. inpanel/public/lib/bootstrap/css/bootstrap.min.css.map +1 -0
  98. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.eot +0 -0
  99. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.svg +288 -0
  100. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf +0 -0
  101. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff +0 -0
  102. inpanel/public/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2 +0 -0
  103. inpanel/public/lib/bootstrap/img/glyphicons-halflings-white.png +0 -0
  104. inpanel/public/lib/bootstrap/img/glyphicons-halflings.png +0 -0
  105. inpanel/public/lib/bootstrap/js/bootstrap.js +2377 -0
  106. inpanel/public/lib/bootstrap/js/bootstrap.min.js +7 -0
  107. inpanel/public/lib/bootstrap/js/npm.js +13 -0
  108. inpanel/public/lib/codemirror/codemirror-compressed.js +1 -0
  109. inpanel/public/lib/codemirror/codemirror.css +173 -0
  110. inpanel/public/lib/jquery/jquery-1.12.4.min.js +5 -0
  111. inpanel/public/lib/md5/md5.js +379 -0
  112. inpanel/public/lib/md5/md5.min.js +1 -0
  113. inpanel/public/partials/database/index.html +172 -0
  114. inpanel/public/partials/database/mysql/dbedit.html +462 -0
  115. inpanel/public/partials/database/mysql/dbnew.html +348 -0
  116. inpanel/public/partials/database/mysql/useredit.html +310 -0
  117. inpanel/public/partials/database/mysql/usernew.html +97 -0
  118. inpanel/public/partials/directives/message.html +20 -0
  119. inpanel/public/partials/directives/navbar.html +25 -0
  120. inpanel/public/partials/directives/selector.html +57 -0
  121. inpanel/public/partials/directives/srvbase.html +72 -0
  122. inpanel/public/partials/directives/srvext.html +34 -0
  123. inpanel/public/partials/directives/srvfile.html +23 -0
  124. inpanel/public/partials/directives/srvinstall.html +34 -0
  125. inpanel/public/partials/directives/srvlog.html +23 -0
  126. inpanel/public/partials/directives/srvminiop.html +28 -0
  127. inpanel/public/partials/directives/srvuninstall.html +38 -0
  128. inpanel/public/partials/directives/srvupdate.html +30 -0
  129. inpanel/public/partials/ecs/account.html +156 -0
  130. inpanel/public/partials/ecs/ecs.html +198 -0
  131. inpanel/public/partials/ecs/index.html +40 -0
  132. inpanel/public/partials/ecs/setings.html +459 -0
  133. inpanel/public/partials/file/file.html +501 -0
  134. inpanel/public/partials/file/trash.html +95 -0
  135. inpanel/public/partials/ftp.html +36 -0
  136. inpanel/public/partials/log.html +83 -0
  137. inpanel/public/partials/login.html +79 -0
  138. inpanel/public/partials/logout.html +22 -0
  139. inpanel/public/partials/main.html +534 -0
  140. inpanel/public/partials/plugins/index.html +55 -0
  141. inpanel/public/partials/plugins/plugins.html +13 -0
  142. inpanel/public/partials/secure.html +13 -0
  143. inpanel/public/partials/service/apache.html +147 -0
  144. inpanel/public/partials/service/cron.html +71 -0
  145. inpanel/public/partials/service/index.html +293 -0
  146. inpanel/public/partials/service/iptables.html +40 -0
  147. inpanel/public/partials/service/lighttpd.html +63 -0
  148. inpanel/public/partials/service/mariadb.html +113 -0
  149. inpanel/public/partials/service/memcache.html +36 -0
  150. inpanel/public/partials/service/minio.html +44 -0
  151. inpanel/public/partials/service/mongodb.html +44 -0
  152. inpanel/public/partials/service/mysql.html +113 -0
  153. inpanel/public/partials/service/named.html +61 -0
  154. inpanel/public/partials/service/nginx.html +296 -0
  155. inpanel/public/partials/service/ntp.html +40 -0
  156. inpanel/public/partials/service/php.html +250 -0
  157. inpanel/public/partials/service/proftpd.html +59 -0
  158. inpanel/public/partials/service/pureftpd.html +59 -0
  159. inpanel/public/partials/service/redis.html +40 -0
  160. inpanel/public/partials/service/samba.html +58 -0
  161. inpanel/public/partials/service/sendmail.html +40 -0
  162. inpanel/public/partials/service/ssh.html +186 -0
  163. inpanel/public/partials/service/tomcat.html +65 -0
  164. inpanel/public/partials/service/vsftpd.html +195 -0
  165. inpanel/public/partials/setting.html +299 -0
  166. inpanel/public/partials/site/apache/site.html +277 -0
  167. inpanel/public/partials/site/index.html +344 -0
  168. inpanel/public/partials/site/nginx/site.html +558 -0
  169. inpanel/public/partials/sorry.html +19 -0
  170. inpanel/public/partials/storage/autofm.html +103 -0
  171. inpanel/public/partials/storage/backup.html +42 -0
  172. inpanel/public/partials/storage/index.html +360 -0
  173. inpanel/public/partials/storage/migrate.html +93 -0
  174. inpanel/public/partials/storage/movedata.html +98 -0
  175. inpanel/public/partials/storage/remote.html +77 -0
  176. inpanel/public/partials/utils/cron.html +486 -0
  177. inpanel/public/partials/utils/firewall.html +266 -0
  178. inpanel/public/partials/utils/index.html +193 -0
  179. inpanel/public/partials/utils/network.html +118 -0
  180. inpanel/public/partials/utils/process.html +239 -0
  181. inpanel/public/partials/utils/repository.html +373 -0
  182. inpanel/public/partials/utils/shell.html +28 -0
  183. inpanel/public/partials/utils/ssl.html +319 -0
  184. inpanel/public/partials/utils/time.html +109 -0
  185. inpanel/public/partials/utils/user.html +359 -0
  186. inpanel/public/robots.txt +2 -0
  187. inpanel/remote.py +109 -0
  188. inpanel/templates/file/preview.html +34 -0
  189. inpanel/templates/index.html +93 -0
  190. inpanel/utils.py +282 -0
  191. inpanel/web.py +1811 -0
  192. inpanel/yum.py +756 -0
  193. inpanel-1.2.1.dist-info/METADATA +399 -0
  194. inpanel-1.2.1.dist-info/RECORD +198 -0
  195. inpanel-1.2.1.dist-info/WHEEL +5 -0
  196. inpanel-1.2.1.dist-info/entry_points.txt +2 -0
  197. inpanel-1.2.1.dist-info/licenses/LICENSE +30 -0
  198. inpanel-1.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,213 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (c) 2020-2026 Jackson Dou
4
+ # All rights reserved.
5
+ #
6
+ # InPanel is distributed under the terms of The New BSD License.
7
+ # The full license can be found in 'LICENSE'.
8
+ '''Module for macOS Firewall Management'''
9
+
10
+ import shutil
11
+ from typing import Tuple, List, Dict
12
+
13
+ from .firewall_base import FirewallManager
14
+ from .system import get_os_family
15
+
16
+
17
+ class MacosFirewallPM(FirewallManager):
18
+ """macOS 防火墙管理器"""
19
+
20
+ SOCKETFILTERFW = '/usr/libexec/ApplicationFirewall/socketfilterfw'
21
+ PFCTL = '/sbin/pfctl'
22
+
23
+ def detect(self) -> bool:
24
+ """检测当前系统是否为 macOS"""
25
+ return get_os_family() == 'darwin'
26
+
27
+ def status(self) -> Tuple[bool, str]:
28
+ """获取防火墙状态"""
29
+ if shutil.which(self.SOCKETFILTERFW):
30
+ success, output = self._run_cmd([self.SOCKETFILTERFW, '--getglobalstate'])
31
+ if success:
32
+ if 'enabled' in output.lower():
33
+ return (True, '运行中')
34
+ elif 'disabled' in output.lower():
35
+ return (True, '已停止')
36
+ return (False, '未知')
37
+
38
+ def enable(self) -> Tuple[bool, str]:
39
+ """启用防火墙(开机自启)"""
40
+ if shutil.which(self.SOCKETFILTERFW):
41
+ success, output = self._run_cmd(['sudo', self.SOCKETFILTERFW, '--setglobalstate', 'on'])
42
+ if success:
43
+ return (True, '防火墙已启用')
44
+ return (False, '无法启用防火墙')
45
+
46
+ def disable(self) -> Tuple[bool, str]:
47
+ """禁用防火墙(关闭开机自启)"""
48
+ if shutil.which(self.SOCKETFILTERFW):
49
+ success, output = self._run_cmd(['sudo', self.SOCKETFILTERFW, '--setglobalstate', 'off'])
50
+ if success:
51
+ return (True, '防火墙已禁用')
52
+ return (False, '无法禁用防火墙')
53
+
54
+ def start(self) -> Tuple[bool, str]:
55
+ """启动防火墙服务"""
56
+ return self.enable()
57
+
58
+ def stop(self) -> Tuple[bool, str]:
59
+ """停止防火墙服务"""
60
+ return self.disable()
61
+
62
+ def restart(self) -> Tuple[bool, str]:
63
+ """重启防火墙服务"""
64
+ self.disable()
65
+ return self.enable()
66
+
67
+ def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
68
+ """添加端口规则 - macOS 使用 pf"""
69
+ if shutil.which(self.PFCTL):
70
+ rule = f'pass in proto {protocol} from any to any port {port}'
71
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'add', rule])
72
+ if not success:
73
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-f', '/dev/stdin'], input=rule)
74
+ if success:
75
+ return (True, f'已添加端口规则: {port}/{protocol}')
76
+ return (False, '无法添加端口规则')
77
+
78
+ def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
79
+ """移除端口规则"""
80
+ if shutil.which(self.PFCTL):
81
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'delete', f'pass in proto {protocol} from any to any port {port}'])
82
+ if success:
83
+ return (True, f'已移除端口规则: {port}/{protocol}')
84
+ return (False, '无法移除端口规则')
85
+
86
+ def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
87
+ """添加IP规则"""
88
+ if shutil.which(self.PFCTL):
89
+ if action == 'allow':
90
+ rule = f'pass in from {ip} to any'
91
+ else:
92
+ rule = f'block in from {ip} to any'
93
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'add', rule])
94
+ if success:
95
+ return (True, f'已添加IP规则: {action} {ip}')
96
+ return (False, '无法添加IP规则')
97
+
98
+ def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
99
+ """移除IP规则"""
100
+ if shutil.which(self.PFCTL):
101
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'delete', f'pass in from {ip} to any'])
102
+ if not success:
103
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-t', 'InPanel', '-T', 'delete', f'block in from {ip} to any'])
104
+ if success:
105
+ return (True, f'已移除IP规则: {ip}')
106
+ return (False, '无法移除IP规则')
107
+
108
+ def remove_app_rule(self, app_path: str) -> Tuple[bool, str]:
109
+ """移除应用规则"""
110
+ if shutil.which(self.SOCKETFILTERFW):
111
+ success, output = self._run_cmd(['sudo', self.SOCKETFILTERFW, '--remove', app_path])
112
+ if success:
113
+ return (True, f'已移除应用规则: {app_path}')
114
+ return (False, '无法移除应用规则')
115
+
116
+ def list_rules(self) -> Tuple[bool, str]:
117
+ """列出所有规则"""
118
+ rules = []
119
+
120
+ if shutil.which(self.SOCKETFILTERFW):
121
+ success, output = self._run_cmd([self.SOCKETFILTERFW, '--listapps'])
122
+ if success:
123
+ rules.append("=== 应用规则 ===")
124
+ rules.append(output)
125
+
126
+ if shutil.which(self.PFCTL):
127
+ success, output = self._run_cmd(['sudo', self.PFCTL, '-s', 'rules'])
128
+ if success:
129
+ rules.append("\n=== PF 端口规则 ===")
130
+ rules.append(output)
131
+
132
+ return (True, '\n'.join(rules) if rules else '无规则')
133
+
134
+ def list_zones(self) -> Tuple[bool, List[str]]:
135
+ """macOS 不使用区域概念"""
136
+ return (True, [])
137
+
138
+ def get_default_zone(self) -> Tuple[bool, str]:
139
+ """macOS 不使用区域概念"""
140
+ return (True, '')
141
+
142
+ def parse_rules(self) -> List[Dict]:
143
+ """解析规则列表为结构化数据"""
144
+ rules = []
145
+ success, output = self.list_rules()
146
+
147
+ if not success:
148
+ return rules
149
+
150
+ lines = output.split('\n')
151
+ app_section = False
152
+ pf_section = False
153
+ current_app_path = ''
154
+
155
+ for line in lines:
156
+ line = line.strip()
157
+ if not line:
158
+ continue
159
+
160
+ if '=== 应用规则 ===' in line:
161
+ app_section = True
162
+ pf_section = False
163
+ continue
164
+ if '=== PF 端口规则 ===' in line:
165
+ app_section = False
166
+ pf_section = True
167
+ continue
168
+
169
+ if app_section and line:
170
+ if 'total number of apps' in line:
171
+ continue
172
+
173
+ import re
174
+ app_match = re.match(r'\d+\s*:\s*(/.+)', line)
175
+ if app_match:
176
+ current_app_path = app_match.group(1).strip()
177
+ app_name = current_app_path.split('/')[-1].replace('.app', '')
178
+ rules.append({
179
+ 'type': 'app',
180
+ 'port': '',
181
+ 'protocol': '',
182
+ 'action': 'allow',
183
+ 'ip': '',
184
+ 'zone': '',
185
+ 'description': app_name,
186
+ 'app_path': current_app_path
187
+ })
188
+ elif 'Allow incoming connections' in line and current_app_path:
189
+ pass
190
+
191
+ if pf_section and line:
192
+ import re
193
+ port_match = re.search(r'port\s+(\d+)', line)
194
+ proto_match = re.search(r'proto\s+(\w+)', line)
195
+ ip_match = re.search(r'from\s+(\d+\.\d+\.\d+\.\d+)', line)
196
+
197
+ rule_type = 'port' if port_match else 'ip'
198
+ port = port_match.group(1) if port_match else ''
199
+ protocol = proto_match.group(1) if proto_match else 'tcp'
200
+ ip = ip_match.group(1) if ip_match else ''
201
+ action = 'allow' if 'pass' in line else 'deny'
202
+
203
+ rules.append({
204
+ 'type': rule_type,
205
+ 'port': port,
206
+ 'protocol': protocol,
207
+ 'action': action,
208
+ 'ip': ip,
209
+ 'zone': '',
210
+ 'description': line
211
+ })
212
+
213
+ return rules
@@ -0,0 +1,128 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (c) 2020-2026 Jackson Dou
4
+ # All rights reserved.
5
+ #
6
+ # InPanel is distributed under the terms of The New BSD License.
7
+ # The full license can be found in 'LICENSE'.
8
+ '''Module for UFW (Uncomplicated Firewall) Management'''
9
+
10
+ import shutil
11
+ from typing import Tuple, List, Dict
12
+
13
+ from .firewall_base import FirewallManager
14
+ from .system import is_debian_family
15
+
16
+
17
+ class UfwPM(FirewallManager):
18
+ """Debian 9-13 / Ubuntu 18.04+ / Linux Mint:ufw"""
19
+
20
+ def detect(self) -> bool:
21
+ if not shutil.which("ufw"):
22
+ return False
23
+ return is_debian_family()
24
+
25
+ def status(self) -> Tuple[bool, str]:
26
+ return self._run_cmd(["ufw", "status", "verbose"])
27
+
28
+ def enable(self) -> Tuple[bool, str]:
29
+ return self._run_cmd(["ufw", "enable"])
30
+
31
+ def disable(self) -> Tuple[bool, str]:
32
+ return self._run_cmd(["ufw", "disable"])
33
+
34
+ def start(self) -> Tuple[bool, str]:
35
+ return self._run_cmd(["ufw", "enable"])
36
+
37
+ def stop(self) -> Tuple[bool, str]:
38
+ return self._run_cmd(["ufw", "disable"])
39
+
40
+ def restart(self) -> Tuple[bool, str]:
41
+ return self._run_cmd(["ufw", "reload"])
42
+
43
+ def add_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
44
+ return self._run_cmd(["ufw", "allow", f"{port}/{protocol}"])
45
+
46
+ def remove_rule(self, port: int, protocol: str = 'tcp', zone: str = '') -> Tuple[bool, str]:
47
+ return self._run_cmd(["ufw", "delete", "allow", f"{port}/{protocol}"])
48
+
49
+ def add_ip_rule(self, ip: str, action: str = 'allow') -> Tuple[bool, str]:
50
+ if action == 'allow':
51
+ return self._run_cmd(["ufw", "allow", "from", ip])
52
+ else:
53
+ return self._run_cmd(["ufw", "deny", "from", ip])
54
+
55
+ def remove_ip_rule(self, ip: str) -> Tuple[bool, str]:
56
+ success1, output1 = self._run_cmd(["ufw", "delete", "allow", "from", ip])
57
+ if not success1:
58
+ success2, output2 = self._run_cmd(["ufw", "delete", "deny", "from", ip])
59
+ return (success2, output2)
60
+ return (success1, output1)
61
+
62
+ def list_rules(self) -> Tuple[bool, str]:
63
+ return self._run_cmd(["ufw", "status"])
64
+
65
+ def list_zones(self) -> Tuple[bool, List[str]]:
66
+ return (False, [])
67
+
68
+ def get_default_zone(self) -> Tuple[bool, str]:
69
+ return (False, "Not supported")
70
+
71
+ def parse_rules(self) -> List[Dict]:
72
+ rules = []
73
+ success, output = self._run_cmd(["ufw", "status", "numbered"])
74
+ if not success:
75
+ return rules
76
+
77
+ lines = output.split('\n')
78
+ for line in lines:
79
+ line = line.strip()
80
+ if not line or line.startswith('Status:') or line.startswith('To') or line.startswith('--'):
81
+ continue
82
+
83
+ parts = line.split()
84
+ if len(parts) < 4:
85
+ continue
86
+
87
+ num = parts[0].rstrip('[')
88
+ target = parts[1]
89
+ action = parts[2]
90
+ source = parts[3]
91
+
92
+ protocol = 'tcp'
93
+ port = ''
94
+ ip = ''
95
+
96
+ if '/' in target:
97
+ port, proto = target.split('/', 1)
98
+ protocol = proto
99
+ elif target.isdigit():
100
+ port = int(target)
101
+ else:
102
+ rules.append({
103
+ 'type': 'service',
104
+ 'service': target,
105
+ 'port': '',
106
+ 'protocol': '',
107
+ 'action': action.lower(),
108
+ 'ip': '',
109
+ 'zone': '',
110
+ 'description': f"{action}: {target} from {source}"
111
+ })
112
+ continue
113
+
114
+ if source != 'Anywhere':
115
+ ip = source
116
+
117
+ rules.append({
118
+ 'type': 'port',
119
+ 'service': '',
120
+ 'port': int(port) if port.isdigit() else port,
121
+ 'protocol': protocol,
122
+ 'action': action.lower(),
123
+ 'ip': ip,
124
+ 'zone': '',
125
+ 'description': f"{action}: {port}/{protocol} from {source}"
126
+ })
127
+
128
+ return rules
inpanel/mod/ftp.py ADDED
@@ -0,0 +1,231 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (c) 2019-2026 Jackson Dou
4
+ # All rights reserved.
5
+ #
6
+ # InPanel is distributed under the terms of the (new) BSD License.
7
+ # The full license can be found in 'LICENSE'.
8
+
9
+ '''Module for FTP'''
10
+
11
+ import time
12
+ from pathlib import Path
13
+ from ftplib import FTP
14
+
15
+
16
+ class FTPClient():
17
+ '''FTP Module'''
18
+
19
+ def __init__(self, host, user, pswd):
20
+ self.ftp = None
21
+ self.host = host
22
+ self.user = user
23
+ self.pswd = pswd
24
+ self.init_client()
25
+
26
+ def __delete__(self, instance):
27
+ self.quit()
28
+
29
+ def init_client(self):
30
+ '''init instance'''
31
+ try:
32
+ self.ftp = FTP(self.host)
33
+ self.ftp.login(self.user, self.pswd)
34
+ print('connected: "%s"' % self.host)
35
+ return self.ftp
36
+ except:
37
+ print('connect failed.')
38
+ print('Please check if the host, user and password is correct.')
39
+ return False
40
+
41
+ def reconnect(self):
42
+ '''reconnect to ftp'''
43
+ try:
44
+ self.ftp.login(self.user, self.pswd)
45
+ print('reconnected: "%s"' % self.host)
46
+ except:
47
+ print('reconnect failed')
48
+ return False
49
+ return True
50
+
51
+ def disconnect(self):
52
+ '''close connection'''
53
+ try:
54
+ self.ftp.close()
55
+ print('disconnect successful')
56
+ except:
57
+ print('disconnect failed')
58
+ return False
59
+ return True
60
+
61
+ def quit(self):
62
+ '''quit and close connection'''
63
+ try:
64
+ self.ftp.quit()
65
+ print('quit successful')
66
+ except:
67
+ print('quit failed')
68
+ return False
69
+ return True
70
+
71
+ def cd(self, path):
72
+ '''change directory'''
73
+ if not path:
74
+ return False
75
+ try:
76
+ return self.ftp.cwd(path)
77
+ except:
78
+ print('change directory failed: "%s"' % path)
79
+ return False
80
+
81
+ def pwd(self):
82
+ '''get current path'''
83
+ try:
84
+ return self.ftp.pwd()
85
+ except:
86
+ print('get path failed')
87
+ return None
88
+
89
+ def ls(self, path='', onlyname=False):
90
+ '''get list of files and directories'''
91
+ try:
92
+ if onlyname is True:
93
+ return self.ftp.nlst()
94
+ return self.ftp.dir(path)
95
+ except:
96
+ print('get list failed: "%s"' % path)
97
+ return None
98
+
99
+ def mkd(self, path):
100
+ '''create a directory, return its full pathname.
101
+ '''
102
+ if not path:
103
+ return False
104
+ try:
105
+ return self.ftp.mkd(path)
106
+ except:
107
+ print('create failed: "%s"' % path)
108
+ return False
109
+
110
+ def rename(self, oldname, newname):
111
+ '''rename a file or directory.'''
112
+ try:
113
+ self.ftp.rename(oldname, newname)
114
+ print('rename successful: from "%s" to "%s"' % (oldname, newname))
115
+ except:
116
+ print('rename failed: from "%s" to "%s"' % (oldname, newname))
117
+ return False
118
+ return True
119
+
120
+ def rm(self, filename):
121
+ '''delete a file or directory.'''
122
+ try:
123
+ if self.ftp.delete(filename):
124
+ print('delete successful: "%s"' % filename)
125
+ return True
126
+ else:
127
+ print('delete failed: "%s"' % filename)
128
+ return False
129
+ except:
130
+ print('delete error: "%s"' % filename)
131
+ return False
132
+
133
+ def rmd(self, filename):
134
+ '''delete a file or directory.'''
135
+ try:
136
+ if self.ftp.delete(filename):
137
+ print('delete successful: "%s"' % filename)
138
+ return True
139
+ else:
140
+ print('delete failed: "%s"' % filename)
141
+ return False
142
+ except:
143
+ print('delete failed: "%s"' % filename)
144
+ return False
145
+
146
+ def upload(self, filepath):
147
+ '''upload local file to FTP'''
148
+ try:
149
+ with open(filepath, 'rb') as source:
150
+ target = Path(filepath).name
151
+ self.ftp.storbinary('STOR %s' % target, source)
152
+ except:
153
+ print('upload failed: "%s"' % filepath)
154
+ return False
155
+ print('upload successful: "%s"' % filepath)
156
+ return True
157
+
158
+ def download(self, filename, localfile=''):
159
+ '''download FTP file to local'''
160
+ localfile = str(Path(filename if not localfile else localfile))
161
+ try:
162
+ with open(localfile, 'wb') as target:
163
+ self.ftp.retrbinary("RETR %s" % filename, target.write)
164
+ except:
165
+ print('download failed: from "%s" to "%s"' % (filename, localfile))
166
+ return False
167
+ print('download successful: from "%s" to "%s"' % (filename, localfile))
168
+ return True
169
+
170
+ def find(self, filename):
171
+ '''find file on FTP'''
172
+ files = self.ftp.nlst()
173
+ if filename in files:
174
+ return True
175
+ else:
176
+ return False
177
+
178
+ def uploadtoftp(address, account, password, source, target):
179
+ try:
180
+ ftp = FTPClient(address, account, password)
181
+ # print('1')
182
+ ftp.cd(str(Path(target)))
183
+ # print('2')
184
+ ftp.upload(source)
185
+ # print('3')
186
+ ftp.quit()
187
+ # print('4')
188
+ return True
189
+ except:
190
+ return False
191
+
192
+ def uploadtoftpa(*args, **kwds):
193
+ time.sleep(5)
194
+ return True
195
+
196
+ def test():
197
+ host = ''
198
+ user = ''
199
+ pswd = ''
200
+ ftp = FTPClient(host, user, pswd)
201
+ # ftp.ls()
202
+ ftp.cd('/Backup')
203
+ # print(ftp.pwd())
204
+ # ftp.ls()
205
+ # ftp.rename('1234567/cc.txt', 'ccaa.txt')
206
+ # ftp.rename('ccaa.txt', '1234567/afasff.txt')
207
+ # ftp.mkd('abc/aaa/ccc') # 创建文件夹
208
+ # ftp.mkd('ccc') # 创建文件夹
209
+ # ftp.upload('./app.icns') # 上传文件
210
+ # ftp.upload('./bbb.txt') # 上传文件
211
+
212
+ # ftp.upload('111.txt') # 上传文件
213
+ # ftp.upload('222.txt') # 上传文件
214
+ # ftp.upload('bbb.txt') # 上传文件
215
+ # ftp.download('111.txt', '111aaa.txt') # 下载文件重命名
216
+ # ftp.download('222.txt', '222aaa.txt') # 下载文件重命名
217
+ # ftp.download('111.txt') # 下载文件覆盖
218
+ # ftp.download('222.txt') # 下载文件覆盖
219
+ # ftp.download('bbb.txt') # 下载文件覆盖
220
+
221
+ # ftp.rm('bbb.txt') # 删除文件
222
+ # ftp.rmd("ccc") # 删除目录
223
+
224
+ print(ftp.find('aaa.txt'))
225
+
226
+
227
+ ftp.quit() # 退出
228
+
229
+
230
+ if __name__ == '__main__':
231
+ test()