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

@ -20,8 +20,8 @@
{ {
"name": "securecrt", "name": "securecrt",
"display": "SecureCRT", "display": "SecureCRT",
"app": "SecureCRT.app", "app": "/Applications/SecureCRT.app/Contents/MacOS/SecureCRT",
"cmdline": "/T /N \"TP#ssh://{real_ip}\" /SSH2 /P {host_port} /PASSWORD **** {user_name}@{host_ip}", "cmdline": "/T /N \"TP#ssh://{real_ip}\" /SSH2 /L {user_name} /PASSWORD **** {host_ip}:{host_port}",
"desc": [] "desc": []
}, },
{ {
@ -40,8 +40,8 @@
{ {
"name": "securefx", "name": "securefx",
"display": "SecureFX", "display": "SecureFX",
"app": "SecureCRT.app", "app": "/Applications/SecureFX.app/Contents/MacOS/SecureFX",
"cmdline": "/T /N \"TP#ssh://{real_ip}\" /SSH2 /P {host_port} /PASSWORD **** {user_name}@{host_ip}", "cmdline": "sftp://{user_name}:****@{host_ip}:{host_port}",
"desc": [] "desc": []
}, },
{ {

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@
#define wrap_c_objc_h #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_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. // for cpp global object initialize.
int cpp_main(void* _self, const char* cfg_file, const char* res_path); 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) start_ssh_client:(NSString*)cmd_line termType:(NSString*)term_type termTheme:(NSString*)term_theme termTitle:(NSString*)term_title;
- (int) select_app:(NSString*)ignore;
@end @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]; 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 { - (void) awakeFromNib {
// The path for the configuration file (by default: ~/.tp_assist.ini) // 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 { - (IBAction)visitWebsite:(id)sender {
NSURL *url = [NSURL URLWithString:@"https://www.tp4a.com/"]; 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; break;
} }
if (cfg.application.empty() || cfg.cmdline.empty()) { // if (cfg.application.empty() || cfg.cmdline.empty()) {
EXLOGE("invalid config, error 6.\n"); // EXLOGE("invalid config, error 6.\n");
return false; // return false;
} // }
return true; return true;
} }
@ -137,6 +137,12 @@ bool TsCfg::_load(const ex_astr& str_json) {
//=================================== //===================================
if(!_parse_app(m_root, "ssh", ssh)) if(!_parse_app(m_root, "ssh", ssh))
return false; 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 0
if (!m_root["ssh"].isObject()) { 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(); ex_astr teleport_ip = jsRoot["teleport_ip"].asCString();
int teleport_port = jsRoot["teleport_port"].asUInt(); 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 real_host_ip = jsRoot["remote_host_ip"].asCString();
ex_astr sid = jsRoot["session_id"].asCString(); ex_astr sid = jsRoot["session_id"].asCString();
ex_astr s_exec; ex_astr s_exec;
ex_astr s_arg;
ex_astrs s_argv; ex_astrs s_argv;
@ -602,13 +606,34 @@ void TsHttpRpc::_rpc_func_run_client(const ex_astr& func_args, ex_astr& buf)
return; 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 else
{ {
// sorry, SFTP not supported yet for macOS. // sorry, SFTP not supported yet for macOS.
_create_json_ret(buf, TPE_NOT_IMPLEMENT); // _create_json_ret(buf, TPE_NOT_IMPLEMENT);
// return;
if(g_cfg.sftp.application.length() == 0) {
_create_json_ret(buf, TPE_NOT_EXISTS);
return; 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) 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; 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()); //---- split s_arg and push to s_argv ---
// ex_replace_all(w_exe_path, _T("{user_name}"), w_sid.c_str()); ex_astr::size_type p1 = 0;
// ex_replace_all(w_exe_path, _T("{real_ip}"), w_real_host_ip.c_str()); ex_astr::size_type p2 = 0;
//ex_replace_all(w_exe_path, _T("{assist_tools_path}"), g_env.m_tools_path.c_str()); 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; Json::Value root_ret;
ex_astr utf8_path = s_exec; ex_astr utf8_path = s_exec;
//ex_wstr2astr(w_exe_path, utf8_path, EX_CODEPAGE_UTF8);
ex_astrs::iterator it = s_argv.begin(); ex_astrs::iterator it = s_argv.begin();
for(; it != s_argv.end(); ++it) { for(; it != s_argv.end(); ++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 += " ";
utf8_path += (*it); utf8_path += (*it);
} }
root_ret["path"] = utf8_path; 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()... // for macOS, Create Process should be fork()/exec()...
pid_t processId; pid_t processId;
if ((processId = fork()) == 0) { if ((processId = fork()) == 0) {
@ -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) { 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) 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 7A7C6C9021973C24006869D9 /* StatusIcon@3X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIcon@3X.png"; sourceTree = "<group>"; };
@ -250,6 +251,7 @@
C149EBEE15D5214600B1F558 = { C149EBEE15D5214600B1F558 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A2EC2C1219863A3009CFA85 /* tp_assist.entitlements */,
7A1818951F8242E900F3C882 /* apple-scripts */, 7A1818951F8242E900F3C882 /* apple-scripts */,
7AA2CD581F6AC0DA0074C92B /* site */, 7AA2CD581F6AC0DA0074C92B /* site */,
C149EBFC15D5214600B1F558 /* Frameworks */, C149EBFC15D5214600B1F558 /* Frameworks */,
@ -346,6 +348,15 @@
attributes = { attributes = {
LastUpgradeCheck = 0930; LastUpgradeCheck = 0930;
ORGANIZATIONNAME = TP4A; ORGANIZATIONNAME = TP4A;
TargetAttributes = {
C149EBF815D5214600B1F558 = {
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
};
};
};
};
}; };
buildConfigurationList = C149EBF315D5214600B1F558 /* Build configuration list for PBXProject "tp_assist" */; buildConfigurationList = C149EBF315D5214600B1F558 /* Build configuration list for PBXProject "tp_assist" */;
compatibilityVersion = "Xcode 10.0"; compatibilityVersion = "Xcode 10.0";