1) Allows blocking and non-blocking operations. Namely, it can work in both synchronous and asynchronous modes. I use HttpRequest and NetworkProxy class' asynchronous design here too, since that design proved very effective. 2) Has OPTİONAL network proxy (HTTP_CONNECT, SOCKS4/4a/5) support, using NetworkProxy class. 3) Takes advantage of U++ streams in file upload and download operations, and uses gates for progress tracking when needed. 4) Supports both MD5 and SHA 1 methods. 5) Has SFtp::DirEntry class for easy parsing of directory entries.
#include <Core/Core.h> #include <SFTP/SFTP.h> using namespace Upp; static const char* CRLF = "\r\n"; // This callback allows getting directory entries one by one. // It is optional. bool ListDirectory(SFtp::DirEntry& e) { String ls = e.GetEntry(); if(!ls.IsEmpty()) // When available, traditiona UNIX style directory listing can be used. Cout() << e.GetEntry() << CRLF; else { Cout() << e.GetName() << CRLF << e.GetUserID() << CRLF << e.GetGroupID() << CRLF << e.GetSize() << CRLF << e.GetLastAccessed() << CRLF << e.GetLastModified() << CRLF << "Permissions:" << CRLF << "R: " << e.IsReadable() << ", " << "W: " << e.IsWritable() << ", " << "X: " << e.IsExecutable() << CRLF; } return false; } CONSOLE_APP_MAIN { // This example demonstrates directory listing (ls) and file download operations, // using a public SFTP test server. // Note that this example uses blocking mode. It is synchronous. // There is also a non-blocking, asynchhronous mode. FileOut wallpaper("/home/genericuser/Wallpapers/SFTP-download-test-wallpaper.jpg"); SFtp::DirList dirlist; // SFtp::DirList is a vector containing SFtp:DirEnty elements. // Enable logging. SFtp::Trace(); SFtp sftp; if(sftp.User("demo-user", "demo-user").Connect("demo.wftpserver.com", 2222)) { // Get server banner. Cout() << sftp.GetBanner() << CRLF; if(sftp.OpenDir("/download") && sftp.ListDir(dirlist, callback(ListDirectory))) { if(sftp.Open("/download/F11_wallpaper_06_1600x1200.jpg", SFtp::READ)) { if(sftp.Get(wallpaper)) { Cout() << "File successfully downloaded.\r\n"; return; } } } } Cout() << "Failed.\r\n" << "Reason: " << sftp.GetErrorDesc() << CRLF; // Instead we could have used the SFtpGet() convenience function. // int rc = SFtpGet(wallpaper, "demo-user", "demo-user", "demo-wftpserver.com", 2222, "/download/F11_wallpaper_06_1600x1200.jpg"); }
As to the license, I'd considered that before chosing libssh2. It has a revised BSD license (http://www.libssh2.org/license.html)
Hope this is ok.
// Async jobs. struct Job { SFtp sftp; FileOut file; String path; int index; int cmd; }; enum Command { OPEN, READ, CLOSE, FINISH }; const char *sftp_server = "demo.wftpserver.com"; const char *sftp_user = "demo-user"; const char *sftp_pass = "demo-user"; const char *remote_file = "/download/F11_wallpaper_06_1600x1200.jpg"; void SFtpExample::Download() { // Initialize and fill an array of Job(s) for(int i = 0; i < 10; i++) { Job& job = jobs.Add(); job.sftp.User(sftp_user, sftp_pass); job.sftp.StartConnect(sftp_server, 2222); job.sftp.WhenDo = THISBACK(UpdateGui); job.file.Open(Format("/home/testuser/picture-%d.jpg", i)); job.cmd = OPEN; job.index = i; } // Actual loop: connects to the server and downloads a file in a concurrent way. while(jobs.GetCount()) { for(int i = 0; i < jobs.GetCount(); i++) { Job& job = jobs[i]; SocketWaitEvent e; e.Add(job.sftp.GetSocket()); e.Wait(10); job.sftp.Do(); if(!job.sftp.InProgress()) { if(job.sftp.IsSuccess()) { switch(job.cmd) { case OPEN: job.sftp.StartOpen(remote_file, SSH::READ); job.path = remote_file; job.cmd = READ; continue; case READ: job.sftp.StartGet(job.file, THISBACK2(DownloadProgress, job.index, job.path)); job.cmd = CLOSE; continue; case CLOSE: job.sftp.StartClose(); job.cmd = FINISH; continue; case FINISH: break; } } else if(job.sftp.IsFailure()) list.Set(job.index, 1, DeQtf(job.sftp.GetErrorDesc())); jobs.Remove(i); list.Remove(i); for(int n = 0; n < jobs.GetCount(); n++) jobs[n].index = n; } } } }
Hello,
Just to give you all an update:
U++ wrapper for libssh2 is in good shape.
SSHSession class: implemened. I borrowed the U++ memory allocation code (re/alloc, free) from Core/SSL package and after a slight modification glued it to the SSH package. (It is working!)
Sftp class: Implemented all the necessary commands (seek/seek64/statvfs not yet implemented).
I don't have access to Windows right now, so the code is currently tested only on an up-to-date arch linux installation (with a KDE/Plasma5 desktop).
Below is a screenshot of a very simple (and easy to write) sftp downloader example with 10 concurrent downloads, demonstrating non-blocking/async operation capabilities.
Below is the actual code responsible for 10 concurrent downloads, demonstrating sftp basic async api (Some parameters are hard coded. I was being lazy.)
// Async jobs. struct Job { SFtp sftp; FileOut file; String path; int index; int cmd; }; enum Command { OPEN, READ, CLOSE, FINISH }; const char *sftp_server = "demo.wftpserver.com"; const char *sftp_user = "demo-user"; const char *sftp_pass = "demo-user"; const char *remote_file = "/download/F11_wallpaper_06_1600x1200.jpg"; void SFtpExample::Download() { // Initialize and fill an array of Job(s) for(int i = 0; i < 10; i++) { Job& job = jobs.Add(); job.sftp.User(sftp_user, sftp_pass); job.sftp.StartConnect(sftp_server, 2222); job.sftp.WhenDo = THISBACK(UpdateGui); job.file.Open(Format("/home/testuser/picture-%d.jpg", i)); job.cmd = OPEN; job.index = i; } // Actual loop: connects to the server and downloads a file in a concurrent way. while(jobs.GetCount()) { for(int i = 0; i < jobs.GetCount(); i++) { Job& job = jobs[i]; SocketWaitEvent e; e.Add(job.sftp.GetSocket()); e.Wait(10); job.sftp.Do(); if(!job.sftp.InProgress()) { if(job.sftp.IsSuccess()) { switch(job.cmd) { case OPEN: job.sftp.StartOpen(remote_file, SSH::READ); job.path = remote_file; job.cmd = READ; continue; case READ: job.sftp.StartGet(job.file, THISBACK2(DownloadProgress, job.index, job.path)); job.cmd = CLOSE; continue; case CLOSE: job.sftp.StartClose(); job.cmd = FINISH; continue; case FINISH: break; } } else if(job.sftp.IsFailure()) list.Set(job.index, 1, DeQtf(job.sftp.GetErrorDesc())); jobs.Remove(i); list.Remove(i); for(int n = 0; n < jobs.GetCount(); n++) jobs[n].index = n; } } } }
P.s. I delayed the upload of the package, since it still has some rough edges to iron-out.
Regards,
Oblivion
I was thinking that it would be a cool demonstration (and a very nice PR CodeProject article) to create actual EDITOR of text files over FTP/FTPS/SFTP in "list of files in the left" style. Not sure I will have enough time for that, but it would be fun.
struct Job { SFtp sftp; FileOut file; int index; }; Array<Job> jobs; //........ const char *sftp_server = "demo.wftpserver.com"; const char *sftp_user = "demo-user"; const char *sftp_password = "demo-user"; const char *remote_path = "/download"; bool SFtpDownloader::ReadDir(Ssh::DirList& ls) { // We'll get the directory listing, using a synchronous (blocking) call. SFtp browser; browser.WhenDo << [&] {ProcessEvents(); }; // GUI should be responsive... if(browser.Connect(sftp_server, 2222, sftp_user, sftp_password)) { // Let us use a convenience method which works on paths, not file descriptors. if(browser.ListDir(remote_path, ls)) return true; } Exclamation(browser.GetErrorDesc()); return false; } void SFtpDownloader::Download() { // Wait for the queue to be processed. if(!jobs.IsEmpty()) return; Ssh::DirList ls; if(!ReadDir(ls)) return; for(int i = 0, j = 0; i < ls.GetCount(); i++) { // We can work on directory entries easily. Ssh::DirEntry& e = ls[i]; const Ssh::Attrs& attrs = e.GetAttrs(); // Let us simply try to download all files with *.jpg extension to the current directory, asynchronously. // Here, thanks to AsyncQueue, asynchronous calls work in a fire-and-forget fashion. Similar to "batch processing". // E.g., any number of Sftp commands/jobs can be queued to be processed without intervention (till they're finished, or failed, of course). if(attrs.IsFile() && GetFileExt(e.GetName()).IsEqual(".jpg")) { Job& job = jobs.Add(); job.index = j; job.file.Open(Format("%s/SFtpExample-%d-%s", GetHomeDirectory(), job.index, e.GetName())); job.sftp.StartConnect(sftp_server, 2222, sftp_user, sftp_password); job.sftp.StartGet(job.file, Format("%s/%s", remote_path, e.GetName()), Ssh::READ, 0755, THISBACK2(DownloadProgress, job.index, e.GetName())); j++; } } // Below loop does asynchronous job processing. while(!jobs.IsEmpty()) { int i = 0; SocketWaitEvent we; we.Add(jobs[i].sftp.GetSocket()); we.Wait(10); while(i < jobs.GetCount()) { SFtp& sftp = jobs[i].sftp; sftp.Do(); if(!sftp.InProgress()) { // For the sake of simplicity, we do not differentiate between success and failure. jobs.Remove(i); break; } ProcessEvents(); i++; } } } //........
Hi Oblivion,
This sounds interesting and might well fit my needs to automate some file transfers. Is it possible to get a hold of a copy of this SFTP client module of yours?
Best regards,
Tom
SSH package for U++ -------------------- SSH package is a libssh2 wrapper for Ultimate++. It aims to be a complete secure shell client solution. (SFtp, Scp, Exec, Terminal, X11Exec, KnownHosts) Currently it is at a technical preview stage, which means it is a work-in-progress, and subject to change. Requirements: - A C++ compiler with at least C++11 support. - libssh2 ssh library (https://www.libssh2.org/) Features and Highlights: - Tight integration with U++. - Support for both synchronous and asynchronous operations with both high level and low level methods. - Allows using U++ style memory [de/re]allocators with libssh2. - Ssh subsystems support RTTI, and share a common interface for polymorphism. Namely, it is possible to store different subsystems in the same U++ container. (e.g. SFtp, Exec, Scp, Shell can be all stored in the same Array.) Todo: - Add known hosts and ssh agents support. - Add more high level methods. - Add ssh X11 support. - Ship libssh2 library with the package. - Documentation. History: ----------------- 2017-06-28: EAGAIN is now properly handled across the subsystems. WhenWrite and WhenRead callbacks are removed in favour of parametrized gates. 2017-06-27: SFtp::StartStop: fixed. 2017-06-27: CleanUp() methods are added to the classes Ssh, SFtp, and Channel. This method utilizes JobQueue's WhenCleanup event. 2017-06-26: U++ memory [de/re]allocators added and made default. initial support for ssh shell, and terminal added. Subsystems now perform a clean up on failure to prevent possible heap leaks. 2017-06-22: Initial release.
[ Ssh -> Main session class (system). Provides connection and authentication mechanism. Although known Hosts support not added yet, it will be available in the following versions. SshSubsystem -> SshSubsystem is base of all SSH protocol based communication subsystems. -------------- SFtp -> Complete, but I'm planning to further enhance it with more high level commannds, once the api stabilized. Channel -> Mostly done. Scp -> Complete. Exec -> Complete. Shell -> Not added yet. Besic support is present. And will be developed durther. Terminal -> Not added yet. Basic support is present. And will be developed further. X11Exec -> Not added yet. Will be added incrementally.
#include <Core/Core.h> #include <SSH/SSH.h> using namespace Upp; // SshExec.cpp: // Demonstrates remote command execution on an ssh channel. // A popular public test server: // ----------------------------- // Host: test.rebex.net // User: user // Password: password // Command: ls -l CONSOLE_APP_MAIN { Ssh session; Cout() << "Hostname: "; auto host = ReadStdIn(); Cout() << "Username: "; auto user = ReadStdIn(); Cout() << "Password: "; auto pass = ReadStdIn(); const int port = 22; if(session.Connect(host, port, user, pass)) { Cout() << "Command: "; auto cmd = ReadStdIn(); Exec xc(session); auto rc = xc(cmd, Cout(), Cerr()); if(xc.IsFailure()) Cerr() << Format("Exec failed. %s", xc.GetErrorDesc()); else Cout() << Format("Remote program exited with code: %d\n", rc); } else Cerr() << session.GetErrorDesc() << '\n'; }
#include <Core/Core.h> #include <SSH/SSH.h> using namespace Upp; // SFtpDir.cpp: // Demonstrates remote directory listing via an sftp channel. // A popular public test server: // ----------------------------- // Host: test.rebex.net // User: user // Password: password // Path: pub/examples CONSOLE_APP_MAIN { Ssh session; Cout() << "Hostname: "; auto host = ReadStdIn(); Cout() << "Username: "; auto user = ReadStdIn(); Cout() << "Password: "; auto pass = ReadStdIn(); Cout() << "Path: "; auto path = ReadStdIn(); const int port = 22; if(session.Connect(host, port, user, pass)) { SFtp sftp(session); SFtp::DirList ls; if(!sftp.ListDir(path, ls)) Exit(sftp.GetError()); for(auto& e : ls) Cout() << e.GetEntry() << "\n"; } else Cerr() << session.GetErrorDesc() << '\n'; }
Re: SFTP or full SSH2 support for U++? Wed, 28 June 2017 15:46
Tom1
Hi Oblivion,
This looks good. However, I may need to wait for the following item on your task list to be completed:
- Ship libssh2 library with the package.
Compiling libssh2 became a bit frustrating on my Windows system, so I decided to rather wait for libssh2 becoming part of the delivery. In my view, the libssh2 should be embedded inside your SSH package in a way that the whole package just gets built and statically linked in Windows like any other package without dependencies. Well, I guess the dependency of Core/SSL should be there to make it work.
Best regards,
Tom
----- SSH ( MSC14 MSC WIN32 ) (2 / 6) Ssh.cpp C:\upp-10804\bazaar\SSH\Ssh.cpp(28): error C2143: syntax error: missing ';' before '.' C:\upp-10804\bazaar\SSH\Ssh.cpp(28): error C2059: syntax error: '.' C:\upp-10804\bazaar\SSH\Ssh.cpp(35): error C2059: syntax error: '.' C:\upp-10804\bazaar\SSH\Ssh.cpp(46): error C2143: syntax error: missing ';' before '.' C:\upp-10804\bazaar\SSH\Ssh.cpp(46): error C2059: syntax error: '.' Channel.cpp C:\upp-10804\bazaar\SSH\Channel.cpp(134): warning C4244: '=': conversion from 'Upp::int64' to 'int', possible loss of data SFtp.cpp C:\upp-10804\bazaar\SSH\SFtp.cpp(269): warning C4244: 'argument': conversion from 'Upp::int64' to 'int', possible loss of data C:\upp-10804\bazaar\SSH\SFtp.cpp(269): warning C4244: 'argument': conversion from 'Upp::int64' to 'std::size_t', possible loss of data C:\upp-10804\bazaar\SSH\SFtp.cpp(277): warning C4554: '&': check operator precedence for possible error; use parentheses to clarify precedence c:\upp-10804\bazaar\ssh\ssh.h(165) : error C4716: 'Upp::SFtp::StartGetStat': must return a value c:\upp-10804\bazaar\ssh\ssh.h(174) : error C4716: 'Upp::SFtp::StartRealPath': must return a value c:\upp-10804\bazaar\ssh\sftp.cpp(231) : error C4716: 'Upp::SFtp::StartListDir': must return a value Exec.cpp Scp.cpp libssh2upp.c c:\upp-10804\bazaar\ssh\libssh2\libssh2_config.h(105): error C2375: 'snprintf': redefinition; different linkage c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(1932): note: see declaration of 'snprintf' c:\upp-10804\bazaar\ssh\libssh2/agent.c(300): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable de precation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(1769): note: see declaration of 'sprintf' c:\upp-10804\bazaar\ssh\libssh2/channel.c(43): fatal error C1083: Cannot open include file: 'unistd.h': No such file or directory Malloc.cpp SSH: 7 file(s) built in (0:09.19), 1313 msecs / file, duration = 9515 msecs, parallelization 100% There were errors. (1:40.53)
Cout() << "Getting file Demo.mo\n"; FileOut fout(AppendFileName(GetDesktopFolder(), "compile_run.sh")); const char *path = "compile_run.sh"; Scp scp(ssh); scp.WhenRead = [=](int64 total, int64 done) { return false; }; scp.WhenWrite = Proxy(scp.WhenRead); if(!scp.Get(fout, path)) Cout() << scp.GetErrorDesc(); fout.Close();
SFtp.cpp C:\upp\bazaar\SSH\SFtp.cpp(102): warning C4267: 'argument': conversion from 'size_t' to 'unsigned int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(136): warning C4267: 'argument': conversion from 'size_t' to 'unsigned int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(151): warning C4267: 'argument': conversion from 'size_t' to 'unsigned int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(181): warning C4267: 'argument': conversion from 'size_t' to 'unsigned int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(237): warning C4267: 'argument': conversion from 'size_t' to 'unsigned int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(252): warning C4267: 'argument': conversion from 'size_t' to 'unsigned int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(269): waring C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(269): warning C4244: 'initializing': conversion from 'ssize_t' to 'int', possible loss of data C:\upp\bazaar\SSH\SFtp.cpp(300): warning C4244: 'initializing': conversion from 'ssize_t' to 'int', possible loss of data
----- SSH ( MSC14 MSC WIN32 ) Ssh.cpp SFtp.cpp Channel.cpp Scp.cpp Exec.cpp Malloc.cpp libssh2upp.c c:\upp-10804\bazaar\ssh\libssh2/agent.c(300): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable de precation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(1769): note: see declaration of 'sprintf' c:\upp-10804\bazaar\ssh\libssh2/channel.c(1157): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(1769): note: see declaration of 'sprintf' c:\upp-10804\bazaar\ssh\libssh2/knownhost.c(957): warning C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable de precation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(207): note: see declaration of 'fopen' c:\upp-10804\bazaar\ssh\libssh2/knownhost.c(1181): warning C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable d eprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(207): note: see declaration of 'fopen' c:\upp-10804\bazaar\ssh\libssh2/pem.c(257): warning C4018: '<=': signed/unsigned mismatch c:\upp-10804\bazaar\ssh\libssh2/userauth.c(541): warning C4996: 'fopen': This function or variable may be unsafe. Consider using fopen_s instead. To disable dep recation, use _CRT_SECURE_NO_WARNINGS. See online help for details. c:/program files/windows kits/10/include/10.0.14393.0/ucrt\stdio.h(207): note: see declaration of 'fopen' Creating library... SSH: 7 file(s) built in (0:13.20), 1886 msecs / file, duration = 13547 msecs, parallelization 95% C:/upp-10804/out/examples-bazaar/SSH/MSC14.\SSH.lib (1624178 B) created in (0:01.96) OK. (0:13.64)
Connecting to Rebex public sftp test server... Unable to request SFTP subsystem drwx------ 2 demo users 0 Oct 27 2015 . drwx------ 2 demo users 0 Oct 27 2015 .. -rw------- 1 demo users 14280 Mar 19 2007 ConsoleClient.png -rw------- 1 demo users 15091 Mar 19 2007 ConsoleClientSmall.png -rw------- 1 demo users 15836 Mar 19 2007 FtpDownloader.png -rw------- 1 demo users 19156 Feb 16 2007 imap-console-client.png -rw------- 1 demo users 36672 Mar 19 2007 KeyGenerator.png -rw------- 1 demo users 24029 Mar 19 2007 KeyGeneratorSmall.png -rw------- 1 demo users 16471 Feb 16 2007 mail-editor.png -rw------- 1 demo users 35414 Feb 16 2007 mail-send-winforms.png -rw------- 1 demo users 49011 Feb 16 2007 mime-explorer.png -rw------- 1 demo users 58024 Mar 19 2007 pocketftp.png -rw------- 1 demo users 20197 Mar 19 2007 pocketftpSmall.png -rw------- 1 demo users 20472 Feb 16 2007 pop3-browser.png -rw------- 1 demo users 11205 Feb 16 2007 pop3-console-client.png -rw------- 1 demo users 407 Mar 23 2007 readme.txt -rw------- 1 demo users 11546 Mar 19 2007 ResumableTransfer.png -rw------- 1 demo users 2635 Mar 19 2007 winceclient.png -rw------- 1 demo users 6146 Mar 19 2007 winceclientSmall.png -rw------- 1 demo users 80000 Mar 19 2007 WinFormClient.png -rw------- 1 demo users 17911 Mar 19 2007 WinFormClientSmall.png Remote command processed. Return code is 0. <--- Finished in (0:01.79), exitcode: 0 --->
- SFtp::Init(), Stop(), and Ssh.WhenWait() do not exist.
// Scp scp1; scp1.Session(session); // Scp channel is "scheduled" to be initialized. There is no need for manual deinitialization. It will be deinitialized at the end of the code block. //Or Scp scp2(session); // Scp channel is "scheduled" to be initialized. There is no need for manual deinitialization. It will be deinitialized at the end of the code block.
bool Get(Stream& out, const String& path, Gate<int64, int64> progress = Null); bool Put(Stream& in, const String& path, long mode, Gate<int64, int64> progress = Null);
Cout() << "Getting file Demo.mo\n"; FileOut fout(AppendFileName(GetDesktopFolder(), "compile_run.sh")); const char *path = "compile_run.sh"; Scp scp(ssh); if(!scp.Get(fout, path, [=](int64 total, int64 done) { return false; })) // <-- Cout() << scp.GetErrorDesc(); fout.Close();
I wonder if "Unable to request SFTP subsystem" was to be expected here.
New Transport API ================= THE PROBLEM The problem in a nutshell is that when an application opens up multiple channels over a single session, those are all using the same socket. If the application is then using select() to wait for traffic (like any sensible app does) and wants to act on the data when select() tells there is something to for example read, what does an application do? With our current API, you have to loop over all the channels and read from them to see if they have data. This effectively makes blocking reads impossible. If the app has many channels in a setup like this, it even becomes slow. (The original API had the libssh2_poll_channel_read() and libssh2_poll() to somewhat overcome this hurdle, but they too have pretty much the same problems plus a few others.) Traffic in the other direction is similarly limited: the app has to try sending to all channels, even though some of them may very well not accept any data at that point. A SOLUTION I suggest we introduce two new helper functions: libssh2_transport_read() - Read "a bunch" of data from the given socket and returns information to the app about what channels that are now readable (ie they will not block when read from). The function can be called over and over and it will repeatedly return info about what channels that are readable at that moment. libssh2_transport_write() - Returns information about what channels that are writable, in the sense that they have windows set from the remote side that allows data to get sent. Writing to one of those channels will not block. Of course, the underlying socket may only accept a certain amount of data, so at the first short return, nothing more should be attempted to get sent until select() (or equivalent) has been used on the master socket again. I haven't yet figured out a sensible API for how these functions should return that info, but if we agree on the general principles I guess we can work that out.
#include "libssh2/libssh2.h" #include "libssh2/libssh2_sftp.h" #include "libssh2/libssh2_publickey.h"
#include "libssh2.h" #include "libssh2_sftp.h" #include "libssh2_publickey.h"
2017-08-11: Initial api reference docs for Ssh, Ssh::SubSystem, SFtp, Scp, Exec, Knownhosts, are added. 2017-07-31: It is now possible to query, get, and set the possible transportation methods and exchange algorithms. Added Ssh::Method(), Ssh::Methods(), Ssh::GetMethod(), Ssh::GetMethods() methods. Ssh::Host class is from now on KnownHosts class. 2017-07-21: Authentication methods (password, public key, keyboard interaction) are properly implemented. From now on it is possible to choose between authentication methods both on initialization, and on-the-fly (i.e. while logging in, using WhenAuth callback). KnownHosts class is added to the package. This class provides basic known hosts support. It is now possible to verify and trust servers.
... if(session.IsSuccess()){ SFtp::DirList list; SFtp sftp(session); sftp.Clear(); sftp.StartListDir(directoryname,list); sftp.StartClose(); if(sftp.Execute()) return true; // Reading the directory successfully proves it exists sftp.Clear(); sftp.StartMakeDir(directoryname,0755); sftp.StartClose(); return sftp.Execute(); } ...
if(session.IsSuccess()){ SFtp::DirList list; SFtp sftp(session); if(!sftp.ListDir((directoryname, list)) return sftp.MakeDir(directoryname, 0755); }
if(session.IsSuccess()){ SFtp::DirList list; SFtp sftp(session); sftp.StartListDir(directoryname, list); while(sftp.Do()); if(sftp.IsFailure()) { sftp.StartMakeDir(directoryname, 0755); while(sftp.Do()); return sftp.IsSucces(); } }
if(session.IsSuccess()){ SFtp::DirList list; SFtp sftp(session); if(!sftp.ListDir(directoryname, list)) return sftp.MakeDir(directoryname, 0755); }
if(session.IsSuccess()){ SFtp::DirList list; SFtp sftp(session); if(!sftp.ListDir(directoryname, list)){ SFtp sftp2(session); return sftp2.MakeDir(directoryname, 0755); } }
(BTW: I think that I have seen IsOK() quite frequently in U++ instead of IsSuccess(), but I'm not sure if these are supposed to mean exactly the same thing.)
1) This version brings a major redesign of overall components. SSH package's classes are redesigned around a core class named SshCore. SshCore allows a uniform interface for both blocking and non-blocking modes of operation. Visible result of this new design is a single set of methods for both modes of operation for each class. Methods starting with "Start" prefix are removed in favor of a simple "NonBlocking()" switch. 2) In accordance with this behaviour, a sort of "feature parity" is successfully kept, thanks to the new design. Namely, in non-blocking mode, any result can be gathered using the new GetResult() method, which returns Value. 3) All SSH package classes now has pick semantics. 4) New design allows creation of higher-level ("complex") SFTP methods where needed. Unlike the simple SFTP methods, which work on file handles, Complex methods take care of (allocate/free) file handles internally. 5) Experimental multithreading support is added (using AsyncWork). This is still at a primitive stage. 6) Network proxy support is added. This new feature uses NetProxy (Http/Socks4/4a/5) package It works through a plugin, provided via Ssh::WhenProxy callback. And it is completely optional. 7) Cancellation mechanism is added. Any operation can be cancelled at any time using the Cancel() method. 8) SFtp::DirEntry class now has a ToString() method. (It will also gain an ToXml() method.) This method will give an output similar to Unix ls command. 9) libssh2 configured to use OpenSSL by default (WinCNG will be made a compile-time option in the next release).
2017-10-17: Class names finalized: Core/base class -> Ssh Session class -> SshSession SFtp class -> SFtp Channel class -> SshChannel Scp class -> Scp Exec class -> SshExec Knownhosts class -> SshHosts SshAgents -> (will be added as such) CreateSFtp(), CreateExec(), CreateScp(), CreateChannel() methods added to SshSession class. Logging further refined. Error management further refined.
void GetFileInfoViaExec(SshSession& session) { DLOG("---- Getting directory listing of " << file << " using SshExec (blocking):"); auto exec = session.CreateExec(); DDUMP(exec(String(cmd) + file, Cout(), Cerr())); }
CONSOLE_APP_MAIN { StdLogSetup(LOG_FILE | LOG_COUT | LOG_TIMESTAMP); // Ssh::Trace(); SshSession session; session.WhenProxy = [=](TcpSocket& sock) { NetProxy proxy(sock, proxy_host, proxy_port); return proxy.Timeout(10000) .Socks5() .Auth(proxy_username, proxy_password) .Connect(ssh_host, ssh_port); }; if(session.Connect(ssh_host, ssh_port, username, password)) { auto exec = session.CreateExec(); DDUMP(exec("ls -l /readme.txt", Cout(), Cerr()); } else Cerr() << session.GetErrorDesc() << "\n"; }
AsyncWork<String> SFtpGet(SshSession& session, const String& path, Gate<int64, int64> progress = Null); AsyncWork<void> SFtpGet(SshSession& session, const String& source, const String& target, Gate<int64, int64> progress = Null);
DLOG("Initializing libssh2..."); and DLOG("Deinitializing libssh2...");
I downloaded this latest version now after using the version from a few months back.
In order to successfully compile the code, I had to comment out the entire contents of SFtpMT.cpp and the following lines in SFtp.h:
AsyncWork<String> SFtpGet(SshSession& session, const String& path, Gate<int64, int64> progress = Null);
AsyncWork<void> SFtpGet(SshSession& session, const String& source, const String& target, Gate<int64, int64> progress = Null);
Then there's one question: Now that the names have changed, is there a replacement for session.IsSuccess() ? (I just removed these calls in my code to see if SFTP still works. It did Smile )
void GetFileSize(SshSession& session) { DLOG("---- Getting file size of " << file << " (non-blocking):"); auto sftp = session.CreateSFtp(); sftp.NonBlocking().GetSize(file); while(sftp.Do()) Sleep(1); if(sftp.IsError()) DDUMP(sftp.GetErrorDesc()); else DDUMP((int64) sftp.GetResult()); // In non-blocking mode, results can be // "harvested" using GetResult() method. } void FileExists(SshSession& session) { DLOG("---- Test if " << file << " exists (non-blocking):"); auto sftp = session.CreateSFtp(); sftp.NonBlocking().FileExists(file); while(sftp.Do()) Sleep(1); if(sftp.IsError()) DDUMP(sftp.GetErrorDesc()); else DDUMP((bool) sftp.GetResult()); // Alternatively (blocking) if(sftp.NonBlocking(false).FileExists(file)) Cout() << file << " exists.\n"; else Cerr() << sftp.GetErrorDesc() << '\n'; }
DLOG("---- Downloading file " << file << " directly to Cout() (blocking):"); auto sftp = session.CreateSFtp(); if(!sftp.Get(file, Cout())) DDUMP(sftp.GetErrorDesc()); else DLOG("Done.");
void GetDirectoryList(SshSession& session) { DLOG("---- Getting directory listing of " << dir << " (blocking):"); auto sftp = session.CreateSFtp(); SFtp::DirList list; if(sftp.ListDir(dir, list)) { for(auto& e : list) DDUMP(e); } else DDUMP(sftp.GetErrorDesc()); }
- Multithreading support for SFtp, Scp, and SshExec classes. - Ssh agent authentication support. - SocketWaitEvents support for non-blocking I/O - Ssh known hosts support (re-added). - API documentation (updated) - SFtp::DirEntry class gained ToXml() method. - Keyboard (challenge/response) authentication (re-added) - Updated libssh2 - Various bugfixes
alkema_jm
index.php?t=getfile&id=5432&private=0LS,
I use upp-win-11459.7z, vs2017 and Windows10 , I downloaded https://svn.code.sf.net/p/ultimatecomponents/svn/, I put the trunk directories in the bazaar directory.
IpAddrInfo addrinfo;
Error is that the variable 'addrinfo' is a type/structure in Ultimate. To solve error, is renaming variable 'addrinfo' in something else for example 'addrinfo2'.