macOS: 支持SecureCRT做SSH客户端,SecureFX做SFTP客户端了。

macOS: 使用Terminal和iTerm2做SSH客户端时,无需用户敲一次回车键了。
pull/130/head
Apex Liu 2018-11-12 00:09:55 +08:00
parent a883f8a80b
commit 0ddea7fabd
10 changed files with 205 additions and 102 deletions

View File

@ -1,27 +1,27 @@
{
"file_version": 3,
"ssh": {
"selected": "terminal",
"available": [
{
"name":"terminal",
"display": "终端(系统自带)",
"app": "Terminal.app",
"cmdline": "Basic",
"ssh": {
"selected": "terminal",
"available": [
{
"name":"terminal",
"display": "终端(系统自带)",
"app": "Terminal.app",
"cmdline": "Basic",
"desc": []
},
{
"name": "iterm2",
"display": "iTerm2",
"app": "iTerm2.app",
},
{
"name": "iterm2",
"display": "iTerm2",
"app": "iTerm2.app",
"cmdline": "Default",
"desc": []
},
},
{
"name": "securecrt",
"display": "SecureCRT",
"app": "SecureCRT.app",
"cmdline": "/T /N \"TP#ssh://{real_ip}\" /SSH2 /P {host_port} /PASSWORD **** {user_name}@{host_ip}",
"app": "/Applications/SecureCRT.app/Contents/MacOS/SecureCRT",
"cmdline": "/T /N \"TP#ssh://{real_ip}\" /SSH2 /L {user_name} /PASSWORD **** {host_ip}:{host_port}",
"desc": []
},
{
@ -31,8 +31,8 @@
"cmdline": "",
"desc": []
}
]
},
]
},
"sftp": {
"selected": "securefx",
@ -40,8 +40,8 @@
{
"name": "securefx",
"display": "SecureFX",
"app": "SecureCRT.app",
"cmdline": "/T /N \"TP#ssh://{real_ip}\" /SSH2 /P {host_port} /PASSWORD **** {user_name}@{host_ip}",
"app": "/Applications/SecureFX.app/Contents/MacOS/SecureFX",
"cmdline": "sftp://{user_name}:****@{host_ip}:{host_port}",
"desc": []
},
{
@ -89,18 +89,18 @@
},
"rdp": {
"selected": "freerdp",
"available": [
{
"name": "FreeRDP",
"display": "FreeRDP",
"app": "",
"selected": "freerdp",
"available": [
{
"name": "FreeRDP",
"display": "FreeRDP",
"app": "",
"cmdline": "",
"desc": [
"建议使用homebrew安装freerdp安装后freerdp默认路径在/usr/local/Cellar/freerdp/x.y.z/bin/xfreerdp",
"首次安装freerdp后需要重新启动计算机"
]
}
]
}
}
]
}
}

View File

@ -29,7 +29,7 @@ on CommandRun(theCmd, theProfile, theTitle)
set profile to theProfile
write text theCmd
delay 0.5
write text "useless"
write text ""
end tell
end tell
end tell
@ -50,7 +50,7 @@ on CommandRun(theCmd, theProfile, theTitle)
set name to theTitle
write text theCmd
delay 0.5
write text "useless"
write text ""
end tell
end tell
end tell
@ -70,7 +70,7 @@ on CommandRun(theCmd, theProfile, theTitle)
set name to theTitle
write text theCmd
delay 0.5
write text "useless"
write text ""
end tell
end tell
end tell

View File

@ -2,7 +2,6 @@ on scriptRun(argsCmd, argsProfile, argsTitle)
set theCmd to (argsCmd)
set theProfile to (argsProfile)
set theTitle to (argsTitle)
set useless to "useless"
CommandRun(theCmd, theProfile, theTitle)
end scriptRun

View File

@ -68,10 +68,11 @@
<div class="form-group form-group-sm">
<label for="ssh-app" class="col-sm-2 control-label"><strong>程序路径:</strong></label>
<div class="col-sm-9">
<div class="input-group">
<input id="ssh-app" type="text" class="form-control input-args" placeholder="客户端可执行程序文件路径" readonly="readonly">
<span class="input-group-btn"><button class="btn btn-sm btn-primary" type="button" id="ssh-select-app">选择...</button></span>
</div>
<input id="ssh-app" type="text" class="form-control input-args" placeholder="客户端可执行程序文件路径">
<!-- <div class="input-group">-->
<!-- <input id="ssh-app" type="text" class="form-control input-args" placeholder="客户端可执行程序文件路径">-->
<!-- <span class="input-group-btn"><button class="btn btn-sm btn-primary" type="button" id="ssh-select-app">选择...</button></span>-->
<!-- </div>-->
</div>
</div>
@ -222,4 +223,4 @@
</body>
</html>
</html>

View File

@ -9,6 +9,7 @@
#define wrap_c_objc_h
int AppDelegate_start_ssh_client (void *_self, const char* cmd_line, const char* term_type, const char* term_theme, const char* term_title);
int AppDelegate_select_app (void *_self);
// for cpp global object initialize.
int cpp_main(void* _self, const char* cfg_file, const char* res_path);

View File

@ -16,5 +16,6 @@
}
- (int) start_ssh_client:(NSString*)cmd_line termType:(NSString*)term_type termTheme:(NSString*)term_theme termTitle:(NSString*)term_title;
- (int) select_app:(NSString*)ignore;
@end

View File

@ -37,6 +37,11 @@ int AppDelegate_start_ssh_client (void *_self, const char* cmd_line, const char*
return [(__bridge id)_self start_ssh_client:cmdLine termType:termType termTheme:termTheme termTitle:termTitle];
}
int AppDelegate_select_app (void *_self) {
NSString* strIgnore = @"";
return [(__bridge id)_self select_app:strIgnore];
}
- (void) awakeFromNib {
// The path for the configuration file (by default: ~/.tp_assist.ini)
@ -173,6 +178,27 @@ int AppDelegate_start_ssh_client (void *_self, const char* cmd_line, const char*
}
}
- (int) select_app:(NSString*)strIgnore {
// NOT WORK
// this function called by ts_http_rpc.c but it run in worker thread.
// once we call select_app from worker thread, the NSOpenPanel alloc crash.
// so we have had to show UI like "post a event and call callback" stuff.
NSOpenPanel *mySelectPanel = [[NSOpenPanel alloc] init];
[mySelectPanel setCanChooseDirectories:YES];
[mySelectPanel setCanChooseFiles:YES];
[mySelectPanel setCanCreateDirectories:YES];
[mySelectPanel setAllowsMultipleSelection:NO];
[mySelectPanel setResolvesAliases:YES];
if([mySelectPanel runModal] == NSOKButton) {
NSURL *ret = [mySelectPanel URL];
NSLog(@"%@", ret.absoluteString);
}
return 0;
}
- (IBAction)visitWebsite:(id)sender {
NSURL *url = [NSURL URLWithString:@"https://www.tp4a.com/"];

View File

@ -115,10 +115,10 @@ bool TsCfg::_parse_app(const Json::Value& m_root, const ex_astr& str_app, APP_CO
break;
}
if (cfg.application.empty() || cfg.cmdline.empty()) {
EXLOGE("invalid config, error 6.\n");
return false;
}
// if (cfg.application.empty() || cfg.cmdline.empty()) {
// EXLOGE("invalid config, error 6.\n");
// return false;
// }
return true;
}
@ -137,6 +137,12 @@ bool TsCfg::_load(const ex_astr& str_json) {
//===================================
if(!_parse_app(m_root, "ssh", ssh))
return false;
if(!_parse_app(m_root, "sftp", sftp))
return false;
if(!_parse_app(m_root, "telnet", telnet))
return false;
if(!_parse_app(m_root, "rdp", rdp))
return false;
#if 0
if (!m_root["ssh"].isObject()) {

View File

@ -457,11 +457,15 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
ex_astr teleport_ip = jsRoot["teleport_ip"].asCString();
int teleport_port = jsRoot["teleport_port"].asUInt();
char _port[64] = {0};
ex_strformat(_port, 64, "%d", teleport_port);
ex_astr str_teleport_port = _port;
ex_astr real_host_ip = jsRoot["remote_host_ip"].asCString();
ex_astr sid = jsRoot["session_id"].asCString();
ex_astr s_exec;
ex_astr s_arg;
ex_astrs s_argv;
@ -602,13 +606,34 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
return;
}
if(g_cfg.ssh.application.length() == 0) {
_create_json_ret(buf, TPE_NOT_EXISTS);
return;
}
s_exec = g_cfg.ssh.application;
s_argv.push_back(s_exec.c_str());
s_arg = g_cfg.ssh.cmdline;
}
else
{
// sorry, SFTP not supported yet for macOS.
_create_json_ret(buf, TPE_NOT_IMPLEMENT);
return;
}
// _create_json_ret(buf, TPE_NOT_IMPLEMENT);
// return;
if(g_cfg.sftp.application.length() == 0) {
_create_json_ret(buf, TPE_NOT_EXISTS);
return;
}
s_exec = g_cfg.sftp.application;
s_argv.push_back(s_exec.c_str());
s_arg = g_cfg.sftp.cmdline;
}
}
else if (pro_type == TP_PROTOCOL_TYPE_TELNET)
{
@ -621,70 +646,67 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
return;
}
// ex_replace_all(w_exe_path, _T("{host_port}"), w_port);
// ex_replace_all(w_exe_path, _T("{host_ip}"), w_teleport_ip.c_str());
// ex_replace_all(w_exe_path, _T("{user_name}"), w_sid.c_str());
// ex_replace_all(w_exe_path, _T("{real_ip}"), w_real_host_ip.c_str());
//ex_replace_all(w_exe_path, _T("{assist_tools_path}"), g_env.m_tools_path.c_str());
//---- split s_arg and push to s_argv ---
ex_astr::size_type p1 = 0;
ex_astr::size_type p2 = 0;
ex_astr tmp = s_arg;
for(;;) {
ex_remove_white_space(tmp, EX_RSC_BEGIN);
if(tmp.empty()) {
break;
}
if(tmp[0] == '"') {
p1 = 1;
p2 = tmp.find('"', p1);
if(p2 == ex_astr::npos) {
_create_json_ret(buf, TPE_PARAM);
return;
}
ex_astr _t;
_t.assign(tmp, p1, p2 - p1);
tmp.erase(0, p2 + 2);
s_argv.push_back(_t);
} else {
p1 = 0;
p2 = tmp.find(' ', p1);
if(p2 == ex_astr::npos) {
s_argv.push_back(tmp);
tmp.clear();
break;
}
ex_astr _t;
_t.assign(tmp, p1, p2 - p1);
tmp.erase(0, p2 + 1);
s_argv.push_back(_t);
}
}
Json::Value root_ret;
ex_astr utf8_path = s_exec;
//ex_wstr2astr(w_exe_path, utf8_path, EX_CODEPAGE_UTF8);
ex_astrs::iterator it = s_argv.begin();
for(; it != s_argv.end(); ++it) {
utf8_path += " ";
utf8_path += (*it);
}
ex_replace_all((*it), "{host_port}", str_teleport_port);
ex_replace_all((*it), "{host_ip}", teleport_ip);
ex_replace_all((*it), "{user_name}", sid);
ex_replace_all((*it), "{real_ip}", real_host_ip);
//ex_replace_all(utf8_path, _T("{assist_tools_path}"), g_env.m_tools_path.c_str());
utf8_path += " ";
utf8_path += (*it);
}
root_ret["path"] = utf8_path;
// if (!CreateProcess(NULL, (wchar_t *)w_exe_path.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
// {
// EXLOGE(_T("CreateProcess() failed. Error=0x%08X.\n %s\n"), GetLastError(), w_exe_path.c_str());
// root_ret["code"] = TPE_START_CLIENT;
// _create_json_ret(buf, root_ret);
// return;
// }
//system(utf8_path.c_str());
//ex_astr __sid;
//ex_wstr2astr(w_sid, __sid);
//execlp("xfreerdp", "-u", __sid.c_str(), "-g", "800x600", "127.0.0.1:52089", NULL);
// FILE *f = popen(utf8_path.c_str(), "r");
// if(f == NULL) {
// root_ret["code"] = TPE_FAILED;
// } else {
// root_ret["code"] = TPE_OK;
// pclose(f);
// }
// {
// int i = 0;
// char** _argv = (char**)calloc(s_argv.size()+1, sizeof(char*));
// if (!_argv)
// return;
//
// for (i = 0; i < s_argv.size(); ++i)
// {
// _argv[i] = ex_strdup(s_argv[i].c_str());
// }
// _argv[i] = NULL;
//
// execv(s_exec.c_str(), _argv);
//
// for(i = 0; i < s_argv.size(); ++i) {
// if(_argv[i] != NULL) {
// free(_argv[i]);
// }
// }
// free(_argv);
//
// }
// for macOS, Create Process should be fork()/exec()...
pid_t processId;
if ((processId = fork()) == 0) {
@ -700,8 +722,8 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
}
_argv[i] = NULL;
execv(s_exec.c_str(), _argv);
execv(s_exec.c_str(), _argv);
for(i = 0; i < s_argv.size(); ++i) {
if(_argv[i] != NULL) {
free(_argv[i]);
@ -752,8 +774,44 @@ void TsHttpRpc::_rpc_func_set_config(const ex_astr& func_args, ex_astr& buf)
}
void TsHttpRpc::_rpc_func_file_action(const ex_astr& func_args, ex_astr& buf) {
_create_json_ret(buf, TPE_NOT_IMPLEMENT);
_create_json_ret(buf, TPE_FAILED);
#if 0
Json::Reader jreader;
Json::Value jsRoot;
if (!jreader.parse(func_args.c_str(), jsRoot)) {
_create_json_ret(buf, TPE_JSON_FORMAT);
return;
}
// if (!jsRoot["action"].isNumeric()) {
// _create_json_ret(buf, TPE_PARAM);
// return;
// }
// int action = jsRoot["action"].asUInt();
AppDelegate_select_app(g_app);
_create_json_ret(buf, TPE_FAILED);
// if (ret) {
// if (action == 1 || action == 2 || action == 3) {
// ex_astr utf8_path;
// ex_wstr2astr(wszReturnPath, utf8_path, EX_CODEPAGE_UTF8);
// Json::Value root;
// root["code"] = TPE_OK;
// root["path"] = utf8_path;
// _create_json_ret(buf, root);
//
// return;
// } else {
// _create_json_ret(buf, TPE_OK);
// return;
// }
// } else {
// _create_json_ret(buf, TPE_DATA);
// return;
// }
#endif
}
void TsHttpRpc::_rpc_func_get_version(const ex_astr& func_args, ex_astr& buf)

View File

@ -76,6 +76,7 @@
7A27E4A61F6A899B004FDE5D /* ts_const.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts_const.h; sourceTree = "<group>"; };
7A27E4A71F6A8EEC004FDE5D /* ts_env.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ts_env.cpp; sourceTree = "<group>"; };
7A27E4A81F6A8EEC004FDE5D /* ts_env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts_env.h; sourceTree = "<group>"; };
7A2EC2C1219863A3009CFA85 /* tp_assist.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = tp_assist.entitlements; sourceTree = "<group>"; };
7A40FFE21F7B2A4500F11697 /* AppDelegate-C-Interface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AppDelegate-C-Interface.h"; sourceTree = "<group>"; };
7A7C6C8F21973C24006869D9 /* StatusIconAlt@3X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIconAlt@3X.png"; sourceTree = "<group>"; };
7A7C6C9021973C24006869D9 /* StatusIcon@3X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIcon@3X.png"; sourceTree = "<group>"; };
@ -250,6 +251,7 @@
C149EBEE15D5214600B1F558 = {
isa = PBXGroup;
children = (
7A2EC2C1219863A3009CFA85 /* tp_assist.entitlements */,
7A1818951F8242E900F3C882 /* apple-scripts */,
7AA2CD581F6AC0DA0074C92B /* site */,
C149EBFC15D5214600B1F558 /* Frameworks */,
@ -346,6 +348,15 @@
attributes = {
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = TP4A;
TargetAttributes = {
C149EBF815D5214600B1F558 = {
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
};
};
};
};
};
buildConfigurationList = C149EBF315D5214600B1F558 /* Build configuration list for PBXProject "tp_assist" */;
compatibilityVersion = "Xcode 10.0";