' +
- '' +
+ '' +
'
' + url + '
' +
'
' +
'
' + actionsLabel + '
' +
@@ -709,6 +825,8 @@ function probeWebshellConnection(conn) {
type: conn.type || 'php',
method: ((conn.method || 'post').toLowerCase() === 'get') ? 'get' : 'post',
cmd_param: conn.cmdParam || '',
+ encoding: webshellConnEncoding(conn),
+ os: webshellConnOS(conn),
command: 'echo 1'
})
})
@@ -3365,6 +3483,8 @@ function execWebshellCommand(conn, command) {
type: conn.type || 'php',
method: (conn.method || 'post').toLowerCase(),
cmd_param: conn.cmdParam || '',
+ encoding: webshellConnEncoding(conn),
+ os: webshellConnOS(conn),
command: command
})
}).then(function (r) { return r.json(); })
@@ -3391,17 +3511,10 @@ function webshellFileListDir(conn, path) {
apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- url: conn.url,
- password: conn.password || '',
- type: conn.type || 'php',
- method: (conn.method || 'post').toLowerCase(),
- cmd_param: conn.cmdParam || '',
- action: 'list',
- path: path
- })
+ body: webshellFileRequestBody(conn, { action: 'list', path: path })
}).then(function (r) { return r.json(); })
.then(function (data) {
+ applyWebshellDetectedOS(conn, data);
if (!data.ok && data.error) {
listEl.innerHTML = '' + escapeHtml(data.error) + '
' + escapeHtml(data.output || '') + '
';
return;
@@ -3497,16 +3610,9 @@ function fetchWebshellDirectoryItems(conn, path) {
return apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- url: conn.url,
- password: conn.password || '',
- type: conn.type || 'php',
- method: (conn.method || 'post').toLowerCase(),
- cmd_param: conn.cmdParam || '',
- action: 'list',
- path: path
- })
+ body: webshellFileRequestBody(conn, { action: 'list', path: path })
}).then(function (r) { return r.json(); }).then(function (data) {
+ applyWebshellDetectedOS(conn, data);
if (!data || data.error || !data.ok) return [];
return parseWebshellListItems(data.output || '');
}).catch(function () {
@@ -3801,7 +3907,7 @@ function webshellFileMkdir(conn, pathInput) {
var name = prompt(wsT('webshell.newDir') || '新建目录', 'newdir');
if (name == null || !name.trim()) return;
var path = base === '.' ? name.trim() : base + '/' + name.trim();
- apiFetch('/api/webshell/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'mkdir', path: path }) })
+ apiFetch('/api/webshell/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: webshellFileRequestBody(conn, { action: 'mkdir', path: path }) })
.then(function (r) { return r.json(); })
.then(function () { webshellFileListDir(conn, base); })
.catch(function () { webshellFileListDir(conn, base); });
@@ -3848,7 +3954,7 @@ function webshellFileUpload(conn, pathInput) {
webshellFileListDir(conn, base);
return;
}
- apiFetch('/api/webshell/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'upload_chunk', path: path, content: base64Chunks[idx], chunk_index: idx }) })
+ apiFetch('/api/webshell/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: webshellFileRequestBody(conn, { action: 'upload_chunk', path: path, content: base64Chunks[idx], chunk_index: idx }) })
.then(function (r) { return r.json(); })
.then(function () { idx++; sendNext(); })
.catch(function () { idx++; sendNext(); });
@@ -3867,7 +3973,7 @@ function webshellFileRename(conn, oldPath, oldName, listEl) {
var parts = oldPath.split('/');
var dir = parts.length > 1 ? parts.slice(0, -1).join('/') + '/' : '';
var newPath = dir + newName.trim();
- apiFetch('/api/webshell/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'rename', path: oldPath, target_path: newPath }) })
+ apiFetch('/api/webshell/file', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: webshellFileRequestBody(conn, { action: 'rename', path: oldPath, target_path: newPath }) })
.then(function (r) { return r.json(); })
.then(function () { webshellFileListDir(conn, document.getElementById('webshell-file-path').value.trim() || '.'); })
.catch(function () { webshellFileListDir(conn, document.getElementById('webshell-file-path').value.trim() || '.'); });
@@ -3906,7 +4012,7 @@ function webshellFileDownload(conn, path) {
apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'read', path: path })
+ body: webshellFileRequestBody(conn, { action: 'read', path: path })
}).then(function (r) { return r.json(); })
.then(function (data) {
var content = (data && data.output) != null ? data.output : (data.error || '');
@@ -3927,7 +4033,7 @@ function webshellFileRead(conn, path, listEl, browsePath) {
apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'read', path: path })
+ body: webshellFileRequestBody(conn, { action: 'read', path: path })
}).then(function (r) { return r.json(); })
.then(function (data) {
const out = (data && data.output) ? data.output : (data.error || '');
@@ -3956,7 +4062,7 @@ function webshellFileEdit(conn, path, listEl) {
apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'read', path: path })
+ body: webshellFileRequestBody(conn, { action: 'read', path: path })
}).then(function (r) { return r.json(); })
.then(function (data) {
const content = (data && data.output) ? data.output : (data.error || '');
@@ -3992,7 +4098,7 @@ function webshellFileWrite(conn, path, content, onDone, listEl) {
apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'write', path: path, content: content })
+ body: webshellFileRequestBody(conn, { action: 'write', path: path, content: content })
}).then(function (r) { return r.json(); })
.then(function (data) {
if (data && !data.ok && data.error && listEl) {
@@ -4011,7 +4117,7 @@ function webshellFileDelete(conn, path, onDone) {
apiFetch('/api/webshell/file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ url: conn.url, password: conn.password || '', type: conn.type || 'php', method: (conn.method || 'post').toLowerCase(), cmd_param: conn.cmdParam || '', action: 'delete', path: path })
+ body: webshellFileRequestBody(conn, { action: 'delete', path: path })
}).then(function (r) { return r.json(); })
.then(function () { if (onDone) onDone(); })
.catch(function () { if (onDone) onDone(); });
@@ -4063,6 +4169,10 @@ function showAddWebshellModal() {
document.getElementById('webshell-type').value = 'php';
document.getElementById('webshell-method').value = 'post';
document.getElementById('webshell-cmd-param').value = '';
+ var osSelEl = document.getElementById('webshell-os');
+ if (osSelEl) osSelEl.value = 'auto';
+ var encSelEl = document.getElementById('webshell-encoding');
+ if (encSelEl) encSelEl.value = 'auto';
document.getElementById('webshell-remark').value = '';
var titleEl = document.getElementById('webshell-modal-title');
if (titleEl) titleEl.textContent = wsT('webshell.addConnection');
@@ -4081,6 +4191,10 @@ function showEditWebshellModal(connId) {
document.getElementById('webshell-type').value = conn.type || 'php';
document.getElementById('webshell-method').value = (conn.method || 'post').toLowerCase();
document.getElementById('webshell-cmd-param').value = conn.cmdParam || '';
+ var osEditEl = document.getElementById('webshell-os');
+ if (osEditEl) osEditEl.value = normalizeWebshellOS(conn.os);
+ var encEditEl = document.getElementById('webshell-encoding');
+ if (encEditEl) encEditEl.value = normalizeWebshellEncoding(conn.encoding);
document.getElementById('webshell-remark').value = conn.remark || '';
var titleEl = document.getElementById('webshell-modal-title');
if (titleEl) titleEl.textContent = wsT('webshell.editConnectionTitle');
@@ -4308,6 +4422,8 @@ function testWebshellConnection() {
var method = ((document.getElementById('webshell-method') || {}).value || 'post').toLowerCase();
var cmdParam = (document.getElementById('webshell-cmd-param') || {}).value;
if (cmdParam && typeof cmdParam.trim === 'function') cmdParam = cmdParam.trim(); else cmdParam = '';
+ var osTag = normalizeWebshellOS((document.getElementById('webshell-os') || {}).value);
+ var encoding = normalizeWebshellEncoding((document.getElementById('webshell-encoding') || {}).value);
var btn = document.getElementById('webshell-test-btn');
if (btn) { btn.disabled = true; btn.textContent = (typeof wsT === 'function' ? wsT('common.refresh') : '刷新') + '...'; }
if (typeof apiFetch === 'undefined') {
@@ -4315,6 +4431,7 @@ function testWebshellConnection() {
alert(wsT('webshell.testFailed') || '连通性测试失败');
return;
}
+ // 连通性使用 Windows/Linux 都识别的最小内建命令作为探测(echo 1 在 cmd 和 sh 下行为等价)
apiFetch('/api/webshell/exec', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@@ -4324,6 +4441,8 @@ function testWebshellConnection() {
type: type,
method: method === 'get' ? 'get' : 'post',
cmd_param: cmdParam || '',
+ encoding: encoding,
+ os: osTag,
command: 'echo 1'
})
})
@@ -4369,12 +4488,14 @@ function saveWebshellConnection() {
var method = ((document.getElementById('webshell-method') || {}).value || 'post').toLowerCase();
var cmdParam = (document.getElementById('webshell-cmd-param') || {}).value;
if (cmdParam && typeof cmdParam.trim === 'function') cmdParam = cmdParam.trim(); else cmdParam = '';
+ var osTag = normalizeWebshellOS((document.getElementById('webshell-os') || {}).value);
+ var encoding = normalizeWebshellEncoding((document.getElementById('webshell-encoding') || {}).value);
var remark = (document.getElementById('webshell-remark') || {}).value;
if (remark && typeof remark.trim === 'function') remark = remark.trim(); else remark = '';
var editIdEl = document.getElementById('webshell-edit-id');
var editId = editIdEl ? editIdEl.value.trim() : '';
- var body = { url: url, password: password, type: type, method: method === 'get' ? 'get' : 'post', cmd_param: cmdParam, remark: remark || url };
+ var body = { url: url, password: password, type: type, method: method === 'get' ? 'get' : 'post', cmd_param: cmdParam, encoding: encoding, os: osTag, remark: remark || url };
if (typeof apiFetch === 'undefined') return;
var reqUrl = editId ? ('/api/webshell/connections/' + encodeURIComponent(editId)) : '/api/webshell/connections';
diff --git a/web/templates/index.html b/web/templates/index.html
index c33e9700..99ebdf0a 100644
--- a/web/templates/index.html
+++ b/web/templates/index.html
@@ -2925,6 +2925,25 @@
+
+
+
+ 决定文件管理/上传使用 Linux 还是 Windows 命令;PHP/JSP 跑在 Windows 上请选 Windows
+
+
+
+
+ 中文 Windows 目标若出现乱码,请切换为 GBK 或 GB18030
+