summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMavridis Philippe <mavridisf@gmail.com>2022-06-24 14:11:03 +0300
committerMavridis Philippe <mavridisf@gmail.com>2022-07-15 13:12:18 +0300
commitfd94618b638534cebf02eacfae104d147c361492 (patch)
treeb525d951150a83dd4370e7dc0c401cbfe4330f7a
parentd4e06b76962198eb64e6c2826d4695248102037c (diff)
downloadtdebase-fd94618b638534cebf02eacfae104d147c361492.tar.gz
tdebase-fd94618b638534cebf02eacfae104d147c361492.zip
Replaced old sftp ioslave with backported version
Source: https://github.com/sandsmark/kde2-kio-sftp-kde4 Licence: GPLv2 or later This resolves issue #276. Signed-off-by: Mavridis Philippe <mavridisf@gmail.com>
-rw-r--r--tdeioslave/sftp/AUTHORS3
-rw-r--r--tdeioslave/sftp/CHANGELOG59
-rw-r--r--tdeioslave/sftp/CMakeL10n.txt3
-rw-r--r--tdeioslave/sftp/CMakeLists.txt27
-rw-r--r--tdeioslave/sftp/DEBUGGING12
-rw-r--r--tdeioslave/sftp/Makefile.am25
-rw-r--r--tdeioslave/sftp/TODO5
-rw-r--r--tdeioslave/sftp/atomicio.cpp67
-rw-r--r--tdeioslave/sftp/atomicio.h39
-rw-r--r--tdeioslave/sftp/ksshprocess.cpp1114
-rw-r--r--tdeioslave/sftp/ksshprocess.h623
-rw-r--r--tdeioslave/sftp/ksshprocesstest.cpp98
-rw-r--r--tdeioslave/sftp/process.cpp493
-rw-r--r--tdeioslave/sftp/process.h148
-rw-r--r--tdeioslave/sftp/sftp.h91
-rw-r--r--tdeioslave/sftp/sftp.protocol4
-rw-r--r--tdeioslave/sftp/sftpfileattr.cpp345
-rw-r--r--tdeioslave/sftp/sftpfileattr.h261
-rw-r--r--tdeioslave/sftp/tdeio_sftp.cpp3337
-rw-r--r--tdeioslave/sftp/tdeio_sftp.h195
20 files changed, 1518 insertions, 5431 deletions
diff --git a/tdeioslave/sftp/AUTHORS b/tdeioslave/sftp/AUTHORS
deleted file mode 100644
index c763d00bc..000000000
--- a/tdeioslave/sftp/AUTHORS
+++ /dev/null
@@ -1,3 +0,0 @@
-Dawit Alemayehu <adawit@kde.org>
-Lucas Fisher <ljfisher@iastate.edu>
-
diff --git a/tdeioslave/sftp/CHANGELOG b/tdeioslave/sftp/CHANGELOG
deleted file mode 100644
index c34cd9dec..000000000
--- a/tdeioslave/sftp/CHANGELOG
+++ /dev/null
@@ -1,59 +0,0 @@
-- add dialog to ask for username
-- rename() causes SSH to die
-- How to handle overwrite?
-- After the user cancels with the stop button, we get ERR_CANNOT_LAUNCH_PROCESS
- errors, until we kill the ioslave. Same thing after trying the wrong passwd
- too many times.
- This is happening because TDEProcess thinks that the ssh process is still running
- even though it exited.
-- How to handle password and caching?
- - Write our own askpass program using kde
- - set env SSH_ASKPASS_PROGRAM before launching
- -how to do this? TDEProcess doesn't give us access to env variables.
- - Our askpass program can probably talk to the tdesu daemon to implement caching.
-- chmod() succeeds, but konqueror always puts permissions to 0 afterwards. The properties
- dialog is right though.
- Nevermind - ftp ioslave does this too! Maybe a bug with konqueror.
-- stat does not give us group and owner names, only numbers. We could cache the uid/name and
- gid/name so we can give names when doing a stat also.
-
-7-13-2001 - ReadLink stopped working. sftp server always retuns a file not found error
- - Need to implement 64 bit file lengths-->write DataStream << for u_int64
- Still need to offer 32 bit size since this is what kde wants. ljf
- - rename() isn't exactly causing ioslave to die. The stat of the file we are
- going to rename is killing the slave. The slave dies in the statEntry() call.
- I don't know what I am putting in the UDS entry that is causing this. ljf
-7-14-2001 - got put, mimetype working ljf
- - fixed readlink problem - I was sending the wrong path. doh! ljf
-7-17-2001 - If the user changes the host, the slave doesn't change host! setHost() is not
- called, nor is another ioslave spawned. I have not investigated the problem
- yet. ljf
-7-21-2001 - got slave working with kde 2.2 cvs
-7-22-2001 - probable solution to getting password prompt -- open with controlling
- but don't connect stdin/out to terminal. duh!
-8-9-2001 - Doh! I haven't kept very good logs. Look at the cvs logs for better info.
- - At this point tdeio_sftp is using KSshProcess which I wrote in order to make
- a standard interface to the various version of ssh out there. So far it is
- working fairly well. We also now report host key changes to the user and
- allow them to choose whether or not to continue. This is a big improvement.
- - Todo: support use of keys and ssh agent
- put()'s resume functionality needs some work
-1-26-2002 - Rewrote put() following the ftp::put() so it should behave the same way
- - increase the size of the data packet we ask for in ::get up to 60k.
- Through-put increases nicely.
- - Call closeConnection() from construction. Keeps from having unused ssh
- processes laying around after failed operations.
-2-19-2002 - get() now emits mimetype, fixes problem with konqi not downloading file for
- viewing in kpart.
- - get port number using getservbyname instead of hard coding it.
-2-27-2002 - testing before committing back to cvs, test with openssh 3, ssh 3
-6-?-2002 - rewrote openConnection() to using new KSshProcess connect proceedures
-7-20-2002 - Don't put up a message box when auth fails because of now or changed key,
- the call to error() will put up the dialog.
- - Connect fails and no more password are prompted for when we get
- ERR_AUTH_FAILED from KSshProcess.
-9-15-2002 - stuff
-9-29-2002 - the last i18n string updates, fixed problem with uploading files to
- openssh server.
-5-8-2003 - check whether operation types are supported by the negotiated sftp
- protocol version
diff --git a/tdeioslave/sftp/CMakeL10n.txt b/tdeioslave/sftp/CMakeL10n.txt
deleted file mode 100644
index f6ee3ab31..000000000
--- a/tdeioslave/sftp/CMakeL10n.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-##### create translation templates ##############
-
-tde_l10n_create_template( "tdeio_sftp" )
diff --git a/tdeioslave/sftp/CMakeLists.txt b/tdeioslave/sftp/CMakeLists.txt
index 62fe0fce3..8967d1ccd 100644
--- a/tdeioslave/sftp/CMakeLists.txt
+++ b/tdeioslave/sftp/CMakeLists.txt
@@ -1,13 +1,10 @@
-#################################################
-#
-# (C) 2010-2011 Serghei Amelian
-# serghei (DOT) amelian (AT) gmail.com
-#
-# Improvements and feedback are welcome
-#
-# This file is released under GPL >= 2
-#
-#################################################
+###########################################
+# #
+# Improvements and feedback are welcome #
+# #
+# This file is released under GPL >= 2 #
+# #
+###########################################
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
@@ -21,21 +18,21 @@ link_directories(
)
-##### other data ################################
+##### other data #########################
tde_create_translated_desktop(
SOURCE sftp.protocol
DESTINATION ${SERVICES_INSTALL_DIR}
- PO_DIR tdeioslave-desktops
+ #PO_DIR tdeioslave-desktops
)
-##### tdeio_sftp (module) #########################
+##### tdeio_sftp (module) ################
set( target tdeio_sftp )
tde_add_kpart( ${target} AUTOMOC
- SOURCES process.cpp atomicio.cpp tdeio_sftp.cpp sftpfileattr.cpp ksshprocess.cpp
- LINK tdeio-shared tdesu-shared
+ SOURCES tdeio_sftp.cpp
+ LINK tdeio-shared
DESTINATION ${PLUGIN_INSTALL_DIR}
)
diff --git a/tdeioslave/sftp/DEBUGGING b/tdeioslave/sftp/DEBUGGING
deleted file mode 100644
index 8e15c91d4..000000000
--- a/tdeioslave/sftp/DEBUGGING
+++ /dev/null
@@ -1,12 +0,0 @@
-DEBUGGING
-
-The best way to debug this slave is to send debug info to a
-file using 'tdedebugDialog --fullmode'. Then you can 'tail -f' the file to
-see debug messages in real-time.
-
-I also suggest getting the openssh source and recompiling sftp-server to
-send messages to the auth log. This can be done in sftp-server.c be defining
-DEBUG_SFTP_SERVER.
-
-You can do the same with the ssh client by finding the two calls to log_init()
-in ssh.c and changing the last argument from 1 to 0 and recompiling.
diff --git a/tdeioslave/sftp/Makefile.am b/tdeioslave/sftp/Makefile.am
deleted file mode 100644
index 512f5f7dd..000000000
--- a/tdeioslave/sftp/Makefile.am
+++ /dev/null
@@ -1,25 +0,0 @@
-## Makefile.am of tdebase/tdeioslave/sftp
-
-INCLUDES = $(all_includes)
-AM_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor
-METASOURCES = AUTO
-
-####### Files
-
-check_PROGRAMS = ksshprocesstest
-
-ksshprocesstest_SOURCES = ksshprocesstest.cpp
-ksshprocesstest_LDADD = $(LIB_TDESYCOCA) ksshprocess.lo process.lo atomicio.lo
-
-kde_module_LTLIBRARIES = tdeio_sftp.la
-
-tdeio_sftp_la_SOURCES = process.cpp atomicio.cpp tdeio_sftp.cpp sftpfileattr.cpp ksshprocess.cpp
-tdeio_sftp_la_LIBADD = $(LIB_TDEIO)
-tdeio_sftp_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
-noinst_HEADERS = atomicio.h tdeio_sftp.h ksshprocess.h process.h sftpfileattr.h sftp.h
-
-kdelnk_DATA = sftp.protocol
-kdelnkdir = $(kde_servicesdir)
-
-messages:
- $(XGETTEXT) *.cpp -o $(podir)/tdeio_sftp.pot
diff --git a/tdeioslave/sftp/TODO b/tdeioslave/sftp/TODO
deleted file mode 100644
index 0f1411317..000000000
--- a/tdeioslave/sftp/TODO
+++ /dev/null
@@ -1,5 +0,0 @@
-TODO:
-=====
-
-- Support for use of public keys, maybe ssh-agent, a key management app, etc.
-
diff --git a/tdeioslave/sftp/atomicio.cpp b/tdeioslave/sftp/atomicio.cpp
deleted file mode 100644
index 057f20fe9..000000000
--- a/tdeioslave/sftp/atomicio.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-//#include "includes.h"
-//RCSID("$OpenBSD: atomicio.c,v 1.9 2001/03/02 18:54:30 deraadt Exp $");
-
-//#include "xmalloc.h"
-#include "atomicio.h"
-#include <unistd.h>
-#include <errno.h>
-#include <kdebug.h>
-
-/*
- * ensure all of data on socket comes through. f==read || f==write
- */
-
-ssize_t atomicio(int fd, char *_s, size_t n, bool read)
-{
- char *s = _s;
- ssize_t res;
- ssize_t pos = 0;
-
- while (n > pos) {
- if( read)
- res = ::read(fd, s + pos, n - pos);
- else
- res = ::write(fd, s + pos, n - pos);
-
- switch (res) {
- case -1:
- kdDebug() << "atomicio(): errno=" << errno << endl;
-#ifdef EWOULDBLOCK
- if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
-#else
- if (errno == EINTR || errno == EAGAIN)
-#endif
- continue;
- case 0:
- return (res);
- default:
- pos += res;
- }
- }
- return (pos);
-}
diff --git a/tdeioslave/sftp/atomicio.h b/tdeioslave/sftp/atomicio.h
deleted file mode 100644
index 4468757d5..000000000
--- a/tdeioslave/sftp/atomicio.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef atomicio_h
-#define atomicio_h
-
-/* $OpenBSD: atomicio.h,v 1.3 2001/03/02 18:54:30 deraadt Exp $ */
-
-/*
- * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-
-/*
- * Ensure all of data on socket comes through. f==read || f==write
- */
-ssize_t atomicio(int fd, char *_s, size_t n, bool read = true);
-
-#endif
diff --git a/tdeioslave/sftp/ksshprocess.cpp b/tdeioslave/sftp/ksshprocess.cpp
deleted file mode 100644
index 0f19126d2..000000000
--- a/tdeioslave/sftp/ksshprocess.cpp
+++ /dev/null
@@ -1,1114 +0,0 @@
-/***************************************************************************
- ksshprocess.cpp - description
- -------------------
- begin : Tue Jul 31 2001
- copyright : (C) 2001 by Lucas Fisher
- email : ljfisher@purdue.edu
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
-/*
- * See the KSshProcess header for examples on use.
- *
- * This class uses a hacked version of the PTYProcess
- * class. This was needed because the tdelibs PTYProcess does not provide
- * access to the pty file descriptor which we need, because ssh prints the
- * password prompt to the pty and reads the password from the pty. I don't
- * feel I know enough about ptys to confidently modify the orignial
- * PTYProcess class.
- *
- * To start ssh we take the arguments the user gave us
- * in the SshOptList and build the ssh command arguments based on the version
- * of ssh we are using. This command and its arguments are passed to
- * PTYProcess for execution. Once ssh is started we scan each line of input
- * from stdin, stderr, and the pty for recognizable strings. The recognizable
- * strings are taken from several string tables. Each table contains a string
- * for each specific version of ssh we support and a string for a generic
- * version of OpenSSH and commercial SSH incase we don't recognized the
- * specific ssh version strings (as when a new SSH version is released after
- * a release of KSshProcess). There are tables for ssh version strings,
- * password prompts, new host key errors, different host key errors,
- * messages than indicate a successful connect, authentication errors, etc.
- * If we find user interaction is necessary, for instance to provide a
- * password or passphrase, we return a err code to the user who can send
- * a message to KSshProcess, using one of several methods, to correct
- * the error.
- *
- * Determining when the ssh connection has successfully authenticationed has
- * proved to be the most difficult challenge. OpenSSH does not print a message
- * on successful authentication, thus the only way to know is to send data
- * and wait for a return. The problem here is sometimes it can take a bit
- * to establish the connection (for example, do to DNS lookups). This means
- * the user may be sitting there waiting for a connection that failed.
- * Instead, ssh is always started with the verbose flag. Then we look for
- * a message that indicates auth succeeded. This is hazardous because
- * debug messages are more likely to change between OpenSSH releases.
- * Thus, we could become incompatible with new OpenSSH releases.
- */
-
-#include <config.h>
-
-#include "ksshprocess.h"
-
-#include <stdio.h>
-#include <errno.h>
-
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-#include <kstandarddirs.h>
-#include <tdelocale.h>
-#include <tqregexp.h>
-
-/*
- * The following are tables of string and regexps we match
- * against the output of ssh. An entry in each array
- * corresponds the the version of ssh found in versionStrs[].
- *
- * The version strings must be ordered in the array from most
- * specific to least specific in cases where the beginning
- * of several version strings are the similar. For example,
- * consider the openssh version strings. The generic "OpenSSH"
- * must be the last of the openssh version strings in the array
- * so that is matched last. We use these generic version strings
- * so we can do a best effor to support unknown ssh versions.
- */
-TQRegExp KSshProcess::versionStrs[] = {
- TQRegExp("OpenSSH_3\\.[6-9]|OpenSSH_[1-9]*[4-9]\\.[0-9]"),
- TQRegExp("OpenSSH"),
- TQRegExp("SSH Secure Shell")
-};
-
-const char * const KSshProcess::passwordPrompt[] = {
- "password:", // OpenSSH
- "password:", // OpenSSH
- "password:" // SSH
-};
-
-const char * const KSshProcess::passphrasePrompt[] = {
- "Enter passphrase for key",
- "Enter passphrase for key",
- "Passphrase for key"
-};
-
-const char * const KSshProcess::authSuccessMsg[] = {
- "Authentication succeeded",
- "ssh-userauth2 successful",
- "Received SSH_CROSS_AUTHENTICATED packet"
-};
-
-const char* const KSshProcess::authFailedMsg[] = {
- "Permission denied (",
- "Permission denied (",
- "Authentication failed."
-};
-
-const char* const KSshProcess::tryAgainMsg[] = {
- "please try again",
- "please try again",
- "adjfhjsdhfdsjfsjdfhuefeufeuefe"
-};
-
-TQRegExp KSshProcess::hostKeyMissingMsg[] = {
- TQRegExp("The authenticity of host|No (DSA|RSA) host key is known for"),
- TQRegExp("The authenticity of host|No (DSA|RSA) host key is known for"),
- TQRegExp("Host key not found from database")
-};
-
-const char* const KSshProcess::continuePrompt[] = {
- "Are you sure you want to continue connecting (yes/no)?",
- "Are you sure you want to continue connecting (yes/no)?",
- "Are you sure you want to continue connecting (yes/no)?"
-};
-
-const char* const KSshProcess::hostKeyChangedMsg[] = {
- "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!",
- "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!",
- "WARNING: HOST IDENTIFICATION HAS CHANGED!"
-};
-
-TQRegExp KSshProcess::keyFingerprintMsg[] = {
- TQRegExp("..(:..){15}"),
- TQRegExp("..(:..){15}"),
- TQRegExp(".....(-.....){10}")
-};
-
-TQRegExp KSshProcess::knownHostsFileMsg[] = {
- TQRegExp("Add correct host key in (.*) to get rid of this message."),
- TQRegExp("Add correct host key in (.*) to get rid of this message."),
- TQRegExp("Add correct host key to \"(.*)\"")
-};
-
-
-// This prompt only applies to commerical ssh.
-const char* const KSshProcess::changeHostKeyOnDiskPrompt[] = {
- "as;jf;sajkfdslkfjas;dfjdsa;fj;dsajfdsajf",
- "as;jf;sajkfdslkfjas;dfjdsa;fj;dsajfdsajf",
- "Do you want to change the host key on disk (yes/no)?"
-};
-
-// We need this in addition the authFailedMsg because when
-// OpenSSH gets a changed host key it will fail to connect
-// depending on the StrictHostKeyChecking option. Depending
-// how this option is set, it will print "Permission denied"
-// and quit, or print "Host key verification failed." and
-// quit. The later if StrictHostKeyChecking is "no".
-// The former if StrictHostKeyChecking is
-// "yes" or explicitly set to "ask".
-TQRegExp KSshProcess::hostKeyVerifyFailedMsg[] = {
- TQRegExp("Host key verification failed\\."),
- TQRegExp("Host key verification failed\\."),
- TQRegExp("Disconnected; key exchange or algorithm? negotiation failed \\(Key exchange failed\\.\\)\\.")
-};
-
-const char * const KSshProcess::connectionClosedMsg[] = {
- "Connection closed by remote host",
- "Connection closed by remote host",
- "Connection closed by remote host"
-};
-
-
-void KSshProcess::SIGCHLD_handler(int) {
- while(waitpid(-1, NULL, WNOHANG) > 0);
-}
-
-void KSshProcess::installSignalHandlers() {
- struct sigaction act;
- memset(&act,0,sizeof(act));
- act.sa_handler = SIGCHLD_handler;
- act.sa_flags = 0
-#ifdef SA_NOCLDSTOP
- | SA_NOCLDSTOP
-#endif
-#ifdef SA_RESTART
- | SA_RESTART
-#endif
- ;
- sigaction(SIGCHLD,&act,NULL);
-}
-
-void KSshProcess::removeSignalHandlers() {
- struct sigaction act;
- memset(&act,0,sizeof(act));
- act.sa_handler = SIG_DFL;
- sigaction(SIGCHLD,&act,NULL);
-}
-
-KSshProcess::KSshProcess()
- : mVersion(UNKNOWN_VER), mConnected(false),
- mRunning(false), mConnectState(0) {
- mSshPath = TDEStandardDirs::findExe(TQString::fromLatin1("ssh"));
- kdDebug(KSSHPROC) << "KSshProcess::KSshProcess(): ssh path [" <<
- mSshPath << "]" << endl;
-
- installSignalHandlers();
-}
-
-KSshProcess::KSshProcess(TQString pathToSsh)
- : mSshPath(pathToSsh), mVersion(UNKNOWN_VER), mConnected(false),
- mRunning(false), mConnectState(0) {
- installSignalHandlers();
-}
-
-KSshProcess::~KSshProcess(){
- disconnect();
- removeSignalHandlers();
- while(waitpid(-1, NULL, WNOHANG) > 0);
-}
-
-bool KSshProcess::setSshPath(TQString pathToSsh) {
- mSshPath = pathToSsh;
- version();
- if( mVersion == UNKNOWN_VER )
- return false;
-
- return true;
-}
-
-KSshProcess::SshVersion KSshProcess::version() {
- TQString cmd;
- cmd = mSshPath+" -V 2>&1";
-
- // Get version string from ssh client.
- FILE *p;
- if( (p = popen(cmd.latin1(), "r")) == NULL ) {
- kdDebug(KSSHPROC) << "KSshProcess::version(): "
- "failed to start ssh: " << strerror(errno) << endl;
- return UNKNOWN_VER;
- }
-
- // Determine of the version from the version string.
- size_t len;
- char buf[128];
- if( (len = fread(buf, sizeof(char), sizeof(buf)-1, p)) == 0 ) {
- kdDebug(KSSHPROC) << "KSshProcess::version(): "
- "Read of ssh version string failed " <<
- strerror(ferror(p)) << endl;
- return UNKNOWN_VER;
- }
- if( pclose(p) == -1 ) {
- kdError(KSSHPROC) << "KSshProcess::version(): pclose failed." << endl;
- }
- buf[len] = '\0';
- TQString ver;
- ver = buf;
- kdDebug(KSSHPROC) << "KSshProcess::version(): "
- "got version string [" << ver << "]" << endl;
-
- mVersion = UNKNOWN_VER;
- for(int i = 0; i < SSH_VER_MAX; i++) {
- if( ver.find(versionStrs[i]) != -1 ) {
- mVersion = (SshVersion)i;
- break;
- }
- }
-
- kdDebug(KSSHPROC) << "KSshPRocess::version(): version number = "
- << mVersion << endl;
-
- if( mVersion == UNKNOWN_VER ) {
- kdDebug(KSSHPROC) << "KSshProcess::version(): "
- "Sorry, I don't know about this version of ssh" << endl;
- mError = ERR_UNKNOWN_VERSION;
- return UNKNOWN_VER;
- }
-
- return mVersion;
-}
-/*
-TQString KSshProcess::versionStr() {
- if( mVersion == UNKNOWN_VER ) {
- version();
- if( mVersion == UNKNOWN_VER )
- return TQString::null;
- }
-
- return TQString::fromLatin1(versionStrs[mVersion]);
-}
-*/
-
-bool KSshProcess::setOptions(const SshOptList& opts) {
- kdDebug(KSSHPROC) << "KSshProcess::setOptions()" << endl;
- mArgs.clear();
- SshOptListConstIterator it;
- TQString cmd, subsystem;
- mPassword = mUsername = mHost = TQString::null;
- TQCString tmp;
- for(it = opts.begin(); it != opts.end(); ++it) {
- //kdDebug(KSSHPROC) << "opt.opt = " << (*it).opt << endl;
- //kdDebug(KSSHPROC) << "opt.str = " << (*it).str << endl;
- //kdDebug(KSSHPROC) << "opt.num = " << (*it).num << endl;
- switch( (*it).opt ) {
- case SSH_VERBOSE:
- mArgs.append("-v");
- break;
-
- case SSH_SUBSYSTEM:
- subsystem = (*it).str;
- break;
-
- case SSH_PORT:
- mArgs.append("-p");
- tmp.setNum((*it).num);
- mArgs.append(tmp);
- mPort = (*it).num;
- break;
-
- case SSH_HOST:
- mHost = (*it).str;
- break;
-
- case SSH_USERNAME:
- mArgs.append("-l");
- mArgs.append((*it).str.latin1());
- mUsername = (*it).str;
- break;
-
- case SSH_PASSWD:
- mPassword = (*it).str;
- break;
-
- case SSH_PROTOCOL:
- if( mVersion <= OPENSSH ) {
- tmp = "Protocol=";
- tmp += TQString::number((*it).num).latin1();
- mArgs.append("-o");
- mArgs.append(tmp);
- }
- else if( mVersion <= SSH ) {
- if( (*it).num == 1 ) {
- mArgs.append("-1");
- }
- // else uses version 2 by default
- }
- break;
-
- case SSH_FORWARDX11:
- tmp = "ForwardX11=";
- tmp += (*it).boolean ? "yes" : "no";
- mArgs.append("-o");
- mArgs.append(tmp);
- break;
-
- case SSH_FORWARDAGENT:
- tmp = "ForwardAgent=";
- tmp += (*it).boolean ? "yes" : "no";
- mArgs.append("-o");
- mArgs.append(tmp);
- break;
-
- case SSH_ESCAPE_CHAR:
- if( (*it).num == -1 )
- tmp = "none";
- else
- tmp = (char)((*it).num);
- mArgs.append("-e");
- mArgs.append(tmp);
- break;
-
- case SSH_OPTION:
- // don't allow NumberOfPasswordPrompts or StrictHostKeyChecking
- // since KSshProcess depends on specific setting of these for
- // preforming authentication correctly.
- tmp = (*it).str.latin1();
- if( tmp.contains("NumberOfPasswordPrompts") ||
- tmp.contains("StrictHostKeyChecking") ) {
- mError = ERR_INVALID_OPT;
- return false;
- }
- else {
- mArgs.append("-o");
- mArgs.append(tmp);
- }
- break;
-
- case SSH_COMMAND:
- cmd = (*it).str;
- break;
-
- default:
- kdDebug(KSSHPROC) << "KSshProcess::setOptions(): "
- "unrecognized ssh opt " << (*it).opt << endl;
- }
- }
-
- if( !subsystem.isEmpty() && !cmd.isEmpty() ) {
- kdDebug(KSSHPROC) << "KSshProcess::setOptions(): "
- "cannot use a subsystem and command at the same time" << endl;
- mError = ERR_CMD_SUBSYS_CONFLICT;
- mErrorMsg = i18n("Cannot specify a subsystem and command at the same time.");
- return false;
- }
-
- // These options govern the behavior of ssh and
- // cannot be defined by the user
- //mArgs.append("-o");
- //mArgs.append("StrictHostKeyChecking=ask");
- mArgs.append("-v"); // So we get a message that the
- // connection was successful
- if( mVersion <= OPENSSH ) {
- // nothing
- }
- else if( mVersion <= SSH ) {
- mArgs.append("-o"); // So we can check if the connection was successful
- mArgs.append("AuthenticationSuccessMsg=yes");
- }
-
- if( mHost.isEmpty() ) {
- kdDebug(KSSHPROC) << "KSshProcess::setOptions(): "
- "a host name must be supplied" << endl;
- return false;
- }
- else {
- mArgs.append(mHost.latin1());
- }
-
- if( !subsystem.isEmpty() ) {
- mArgs.append("-s");
- mArgs.append(subsystem.latin1());
- }
-
- if( !cmd.isEmpty() ) {
- mArgs.append(cmd.latin1());
- }
-
- return true;
-}
-
-void KSshProcess::printArgs() {
- TQValueListIterator<TQCString> it;
- for( it = mArgs.begin(); it != mArgs.end(); ++it) {
- kdDebug(KSSHPROC) << "arg: " << *it << endl;
- }
-}
-
-
-int KSshProcess::error(TQString& msg) {
- kdDebug(KSSHPROC) << "KSshProcess::error()" << endl;
- kdDebug() << mErrorMsg << endl;
- msg = mErrorMsg;
- return mError;
-}
-
-void KSshProcess::kill(int signal) {
- int pid = ssh.pid();
-
- kdDebug(KSSHPROC) << "KSshProcess::kill(signal:" << signal
- << "): ssh pid is " << pid << endl;
- kdDebug(KSSHPROC) << "KSshPRocess::kill(): we are "
- << (mConnected ? "" : "not ") << "connected" << endl;
- kdDebug(KSSHPROC) << "KSshProcess::kill(): we are "
- << (mRunning ? "" : "not ") << "running a ssh process" << endl;
-
- if( mRunning && pid > 1 ) {
- // Kill the child process...
- if ( ::kill(pid, signal) == 0 ) {
- // clean up if we tried to kill the process
- if( signal == SIGTERM || signal == SIGKILL ) {
- while(waitpid(-1, NULL, WNOHANG) > 0);
- mConnected = false;
- mRunning = false;
- }
- }
- else
- kdDebug(KSSHPROC) << "KSshProcess::kill(): kill failed" << endl;
- }
- else
- kdDebug(KSSHPROC) << "KSshProcess::kill(): "
- "Refusing to kill ssh process" << endl;
-}
-
-
-
-/**
- * Try to open an ssh connection.
- * SSH prints certain messages to certain file descriptiors:
- * passwordPrompt - pty
- * passphrasePrompt - pty
- * authSuccessMsg - stderr (OpenSSH),
- * authFailedMsg - stderr
- * hostKeyMissing - stderr
- * hostKeyChanged - stderr
- * continuePrompt - stderr
- *
- * We will use a select to wait for a line on each descriptor. Then get
- * each line that available and take action based on it. The type
- * of messages we are looking for and the action we take on each
- * message are:
- * passwordPrompt - Return false, set error to ERR_NEED_PASSWD.
- * On the next call to connect() we expect a password
- * to be available.
- *
- * passpharsePrompt - Return false, set error to ERR_NEED_PASSPHRASE.
- * On the next call to connect() we expect a
- * passphrase to be available.
- *
- * authSuccessMsg - Return true, as we have successfully established a
- * ssh connection.
- *
- * authFailedMsg - Return false, set error to ERR_AUTH_FAILED. We
- * were unable to authenticate the connection given
- * the available authentication information.
- *
- * hostKeyMissing - Return false, set error to ERR_NEW_HOST_KEY. Caller
- * must call KSshProcess.acceptHostKey(bool) to accept
- * or reject the key before calling connect() again.
- *
- * hostKeyChanged - Return false, set error to ERR_DIFF_HOST_KEY. Caller
- * must call KSshProcess.acceptHostKey(bool) to accept
- * or reject the key before calling connect() again.
- *
- * continuePrompt - Send 'yes' or 'no' to accept or reject a key,
- * respectively.
- *
- */
-
-
-void KSshProcess::acceptHostKey(bool accept) {
- kdDebug(KSSHPROC) << "KSshProcess::acceptHostKey(accept:"
- << accept << ")" << endl;
- mAcceptHostKey = accept;
-}
-
-void KSshProcess::setPassword(TQString password) {
- kdDebug(KSSHPROC) << "KSshProcess::setPassword(password:xxxxxxxx)" << endl;
- mPassword = password;
-}
-
-TQString KSshProcess::getLine() {
- static TQStringList buffer;
- TQString line = TQString::null;
- TQCString ptyLine, errLine;
-
- if( buffer.empty() ) {
- // PtyProcess buffers lines. First check that there
- // isn't something on the PtyProces buffer or that there
- // is not data ready to be read from the pty or stderr.
- ptyLine = ssh.readLineFromPty(false);
- errLine = ssh.readLineFromStderr(false);
-
- // If PtyProcess did have something for us, get it and
- // place it in our line buffer.
- if( ! ptyLine.isEmpty() ) {
- buffer.prepend(TQString(ptyLine));
- }
-
- if( ! errLine.isEmpty() ) {
- buffer.prepend(TQString(errLine));
- }
-
- // If we still don't have anything in our buffer so there must
- // not be anything on the pty or stderr. Setup a select()
- // to wait for some data from SSH.
- // Hack around select() failure on newer systems
- unsigned long milliseconds = 0;
- while ((buffer.size() == 0) && (milliseconds < (60*1000))) {
- //kdDebug(KSSHPROC) << "KSshProcess::getLine(): " <<
- // "Line buffer empty, calling select() to wait for data." << endl;
- int errfd = ssh.stderrFd();
- int ptyfd = ssh.fd();
- fd_set rfds;
- fd_set efds;
- struct timeval tv;
-
- // find max file descriptor
- int maxfd = ptyfd > errfd ? ptyfd : errfd;
-
- FD_ZERO(&rfds);
- FD_SET(ptyfd, &rfds); // Add pty file descriptor
- FD_SET(errfd, &rfds); // Add std error file descriptor
-
- FD_ZERO(&efds);
- FD_SET(ptyfd, &efds);
- FD_SET(errfd, &efds);
-
- tv.tv_sec = 60; tv.tv_usec = 0; // 60 second timeout
-
- // Wait for a message from ssh on stderr or the pty.
- int ret = -1;
- do
- ret = ::select(maxfd+1, &rfds, NULL, &efds, &tv);
- while( ret == -1 && errno == EINTR );
-
- // Handle any errors from select
- if( ret == 0 ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): " <<
- "timed out waiting for a response" << endl;
- mError = ERR_TIMED_OUT;
- return TQString::null;
- }
- else if( ret == -1 ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- << "select error: " << strerror(errno) << endl;
- mError = ERR_INTERNAL;
- return TQString::null;
- }
-
- // We are not respecting any type of order in which the
- // lines were received. Who knows whether pty or stderr
- // had data on it first.
- if( FD_ISSET(ptyfd, &rfds) ) {
- ptyLine = ssh.readLineFromPty(false);
- if (ptyLine.size() > 0) {
- buffer.prepend(TQString(ptyLine));
- }
- //kdDebug(KSSHPROC) << "KSshProcess::getLine(): "
- // "line from pty -" << ptyLine << endl;
- }
-
- if( FD_ISSET(errfd, &rfds) ) {
- errLine = ssh.readLineFromStderr(false);
- if (errLine.size() > 0) {
- buffer.prepend(TQString(errLine));
- }
- //kdDebug(KSSHPROC) << "KSshProcess::getLine(): "
- // "line from err -" << errLine << endl;
- }
-
- if( FD_ISSET(ptyfd, &efds) ) {
- kdDebug(KSSHPROC) << "KSshProcess::getLine(): "
- "Exception on pty file descriptor." << endl;
- }
-
- if( FD_ISSET(errfd, &efds) ) {
- kdDebug(KSSHPROC) << "KSshProcess::getLine(): "
- "Exception on std err file descriptor." << endl;
- }
-
- if (buffer.size() == 0) {
- milliseconds++;
- usleep(1000);
- }
- }
- }
-
- // We should have something in our buffer now.
- // Return the last line.
- //it = buffer.end();
- //line = *it;
- //buffer.remove(it);
-
- line = buffer.last();
- buffer.pop_back();
-
- if( line.isNull() && buffer.count() > 0 ) {
- line = buffer.last();
- buffer.pop_back();
- }
-
-// kdDebug(KSSHPROC) << "KSshProcess::getLine(): " <<
-// buffer.count() << " lines in buffer" << endl;
- kdDebug(KSSHPROC) << "KSshProcess::getLine(): "
- "ssh: " << line << endl;
-
-
- return line;
-}
-
-// All the different states we could go through while trying to connect.
-enum sshConnectState {
- STATE_START, STATE_TRY_PASSWD, STATE_WAIT_PROMPT, STATE_NEW_KEY_CONTINUE,
- STATE_DIFF_KEY_CONTINUE, STATE_FATAL, STATE_WAIT_CONTINUE_PROMPT,
- STATE_SEND_CONTINUE, STATE_AUTH_FAILED, STATE_NEW_KEY_WAIT_CONTINUE,
- STATE_DIFF_KEY_WAIT_CONTINUE, STATE_TRY_PASSPHRASE
-};
-
-// Print the state as a string. Good for debugging
-const char* stateStr(int state) {
- switch(state) {
- case STATE_START:
- return "STATE_START";
- case STATE_TRY_PASSWD:
- return "STATE_TRY_PASSWD";
- case STATE_WAIT_PROMPT:
- return "STATE_WAIT_PROMPT";
- case STATE_NEW_KEY_CONTINUE:
- return "STATE_NEW_KEY_CONTINUE";
- case STATE_DIFF_KEY_CONTINUE:
- return "STATE_DIFF_KEY_CONTINUE";
- case STATE_FATAL:
- return "STATE_FATAL";
- case STATE_WAIT_CONTINUE_PROMPT:
- return "STATE_WAIT_CONTINUE_PROMPT";
- case STATE_SEND_CONTINUE:
- return "STATE_SEND_CONTINE";
- case STATE_AUTH_FAILED:
- return "STATE_AUTH_FAILED";
- case STATE_NEW_KEY_WAIT_CONTINUE:
- return "STATE_NEW_KEY_WAIT_CONTINUE";
- case STATE_DIFF_KEY_WAIT_CONTINUE:
- return "STATE_DIFF_KEY_WAIT_CONTINUE";
- case STATE_TRY_PASSPHRASE:
- return "STATE_TRY_PASSPHRASE";
- }
- return "UNKNOWN";
-}
-
-bool KSshProcess::connect() {
- if( mVersion == UNKNOWN_VER ) {
- // we don't know the ssh version yet, so find out
- version();
- if( mVersion == -1 ) {
- return false;
- }
- }
-
- // We'll put a limit on the number of state transitions
- // to ensure we don't go out of control.
- int transitionLimit = 500;
-
- while(--transitionLimit) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- << "Connect state " << stateStr(mConnectState) << endl;
-
- TQString line; // a line from ssh
- TQString msgBuf; // buffer for important messages from ssh
- // which are to be returned to the user
-
- switch(mConnectState) {
- // STATE_START:
- // Executes the ssh binary with the options provided. If no options
- // have been specified, sets error and returns false. Continue to
- // state 1 if execution is successful, otherwise set error and
- // return false.
- case STATE_START:
- // reset some key values to safe values
- mAcceptHostKey = false;
- mKeyFingerprint = TQString::null;
- mKnownHostsFile = TQString::null;
-
- if( mArgs.isEmpty() ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): ssh options "
- "need to be set first using setArgs()" << endl;
- mError = ERR_NO_OPTIONS;
- mErrorMsg = i18n("No options provided for ssh execution.");
- return false;
- }
-
- if( ssh.exec(mSshPath.latin1(), mArgs) ) {
- kdDebug(KSSHPROC) <<
- "KSshProcess::connect(): ssh exec failed" << endl;
- mError = ERR_CANNOT_LAUNCH;
- mErrorMsg = i18n("Failed to execute ssh process.");
- return false;
- }
-
- kdDebug(KSSHPROC) << "KSshPRocess::connect(): ssh pid = " << ssh.pid() << endl;
-
- // set flag to indicate what have started a ssh process
- mRunning = true;
- mConnectState = STATE_WAIT_PROMPT;
- break;
-
- // STATE_WAIT_PROMPT:
- // Get a line of input from the ssh process. Check the contents
- // of the line to determine the next state. Ignore the line
- // if we don't recognize its contents. If the line contains
- // the continue prompt, we have an error since we should never
- // get that line in this state. Set ERR_INVALID_STATE error
- // and return false.
- case STATE_WAIT_PROMPT:
- line = getLine();
- if( line.isNull() ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- "Got null line in STATE_WAIT_PROMPT." << endl;
- mError = ERR_INTERACT;
- mErrorMsg =
- i18n("Error encountered while talking to ssh.");
- mConnectState = STATE_FATAL;
- }
- else if( line.find(TQString::fromLatin1(passwordPrompt[mVersion]), 0, false) != -1 ) {
- mConnectState = STATE_TRY_PASSWD;
- }
- else if( line.find(passphrasePrompt[mVersion]) != -1 ) {
- mConnectState = STATE_TRY_PASSPHRASE;
- }
- else if( line.find(authSuccessMsg[mVersion]) != -1 ) {
- return true;
- }
- else if( line.find(authFailedMsg[mVersion]) != -1
- && line.find(tryAgainMsg[mVersion]) == -1 ) {
- mConnectState = STATE_AUTH_FAILED;
- }
- else if( line.find(hostKeyMissingMsg[mVersion]) != -1 ) {
- mConnectState = STATE_NEW_KEY_WAIT_CONTINUE;
- }
- else if( line.find(hostKeyChangedMsg[mVersion]) != -1 ) {
- mConnectState = STATE_DIFF_KEY_WAIT_CONTINUE;
- }
- else if( line.find(continuePrompt[mVersion]) != -1 ) {
- //mConnectState = STATE_SEND_CONTINUE;
- kdDebug(KSSHPROC) << "KSshProcess:connect(): "
- "Got continue prompt where we shouldn't (STATE_WAIT_PROMPT)"
- << endl;
- mError = ERR_INTERACT;
- mErrorMsg =
- i18n("Error encountered while talking to ssh.");
- }
- else if( line.find(connectionClosedMsg[mVersion]) != -1 ) {
- mConnectState = STATE_FATAL;
- mError = ERR_CLOSED_BY_REMOTE_HOST;
- mErrorMsg = i18n("Connection closed by remote host.");
- }
- else if( line.find(changeHostKeyOnDiskPrompt[mVersion]) != -1 ) {
- // always say yes to this. It always comes after commerical ssh
- // prints a "continue to connect prompt". We assume that if the
- // user choose to continue, then they also want to save the
- // host key to disk.
- ssh.writeLine("yes");
- }
- else {
- // ignore line
- }
- break;
-
- // STATE_TRY_PASSWD:
- // If we have password send it to the ssh process, else
- // set error ERR_NEED_PASSWD and return false to the caller.
- // The caller then must then call KSshProcess::setPassword(TQString)
- // before calling KSshProcess::connect() again.
- //
- // Almost exactly liek STATE_TRY_PASSPHRASE. Check there if you
- // make changes here.
- case STATE_TRY_PASSWD:
- // We have a password prompt waiting for us to supply
- // a password. Send that password to ssh. If the caller
- // did not supply a password like we asked, then ask
- // again.
- if( !mPassword.isEmpty() ) {
-// ssh.WaitSlave();
- ssh.writeLine(mPassword.latin1());
-
- // Overwrite the password so it isn't in memory.
- mPassword.fill(TQChar('X'));
-
- // Set the password to null so we will request another
- // password if this one fails.
- mPassword = TQString::null;
-
- mConnectState = STATE_WAIT_PROMPT;
- }
- else {
- kdDebug(KSSHPROC) << "KSshProcess::connect() "
- "Need password from caller." << endl;
- // The caller needs to supply a password before
- // connecting can continue.
- mError = ERR_NEED_PASSWD;
- mErrorMsg = i18n("Please supply a password.");
- mConnectState = STATE_TRY_PASSWD;
- return false;
- }
- break;
-
- // STATE_TRY_KEY_PASSPHRASE:
- // If we have passphrase send it to the ssh process, else
- // set error ERR_NEED_PASSPHRASE and return false to the caller.
- // The caller then must then call KSshProcess::setPassword(TQString)
- // before calling KSshProcess::connect() again.
- //
- // Almost exactly like STATE_TRY_PASSWD. The only difference is
- // the error we set if we don't have a passphrase. We duplicate
- // this code to keep in the spirit of the state machine.
- case STATE_TRY_PASSPHRASE:
- // We have a passphrase prompt waiting for us to supply
- // a passphrase. Send that passphrase to ssh. If the caller
- // did not supply a passphrase like we asked, then ask
- // again.
- if( !mPassword.isEmpty() ) {
-// ssh.WaitSlave();
- ssh.writeLine(mPassword.latin1());
-
- // Overwrite the password so it isn't in memory.
- mPassword.fill(TQChar('X'));
-
- // Set the password to null so we will request another
- // password if this one fails.
- mPassword = TQString::null;
-
- mConnectState = STATE_WAIT_PROMPT;
- }
- else {
- kdDebug(KSSHPROC) << "KSshProcess::connect() "
- "Need passphrase from caller." << endl;
- // The caller needs to supply a passphrase before
- // connecting can continue.
- mError = ERR_NEED_PASSPHRASE;
- mErrorMsg = i18n("Please supply the passphrase for "
- "your SSH private key.");
- mConnectState = STATE_TRY_PASSPHRASE;
- return false;
- }
- break;
-
- // STATE_AUTH_FAILED:
- // Authentication has failed. Tell the caller by setting the
- // ERR_AUTH_FAILED error and returning false. If
- // auth has failed then ssh should have exited, but
- // we will kill it to make sure.
- case STATE_AUTH_FAILED:
- mError = ERR_AUTH_FAILED;
- mErrorMsg = i18n("Authentication to %1 failed").arg(mHost);
- mConnectState = STATE_FATAL;
- break;
-
- // STATE_NEW_KEY_WAIT_CONTINUE:
- // Grab lines from ssh until we get a continue prompt or a auth
- // denied. We will get the later if StrictHostKeyChecking is set
- // to yes. Go to STATE_NEW_KEY_CONTINUE if we get a continue prompt.
- case STATE_NEW_KEY_WAIT_CONTINUE:
- line = getLine();
- if( line.isNull() ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- "Got null line in STATE_NEW_KEY_WAIT_CONTINUE." << endl;
- mError = ERR_INTERACT;
- mErrorMsg =
- i18n("Error encountered while talking to ssh.");
- mConnectState = STATE_FATAL;
- }
- else if( (line.find(authFailedMsg[mVersion]) != -1
- && line.find(tryAgainMsg[mVersion]) == -1)
- || line.find(hostKeyVerifyFailedMsg[mVersion]) != -1 ) {
- mError = ERR_AUTH_FAILED_NEW_KEY;
- mErrorMsg = i18n(
- "The identity of the remote host '%1' could not be verified "
- "because the host's key is not in the \"known hosts\" file."
- ).arg(mHost);
-
- if( mKnownHostsFile.isEmpty() ) {
- mErrorMsg += i18n(
- " Manually, add the host's key to the \"known hosts\" "
- "file or contact your administrator."
- );
- }
- else {
- mErrorMsg += i18n(
- " Manually, add the host's key to %1 "
- "or contact your administrator."
- ).arg(mKnownHostsFile);
- }
-
- mConnectState = STATE_FATAL;
- }
- else if( line.find(continuePrompt[mVersion]) != -1 ) {
- mConnectState = STATE_NEW_KEY_CONTINUE;
- }
- else if( line.find(connectionClosedMsg[mVersion]) != -1 ) {
- mConnectState = STATE_FATAL;
- mError = ERR_CLOSED_BY_REMOTE_HOST;
- mErrorMsg = i18n("Connection closed by remote host.");
- }
- else if( line.find(keyFingerprintMsg[mVersion]) != -1 ) {
- mKeyFingerprint = keyFingerprintMsg[mVersion].cap();
- kdDebug(KSSHPROC) << "Found key fingerprint: " << mKeyFingerprint << endl;
- mConnectState = STATE_NEW_KEY_WAIT_CONTINUE;
- }
- else {
- // ignore line
- }
- break;
-
-
- // STATE_NEW_KEY_CONTINUE:
- // We got a continue prompt for the new key message. Set the error
- // message to reflect this, return false and hope for caller response.
- case STATE_NEW_KEY_CONTINUE:
- mError = ERR_NEW_HOST_KEY;
- mErrorMsg = i18n(
- "The identity of the remote host '%1' could not be "
- "verified. The host's key fingerprint is:\n%2\nYou should "
- "verify the fingerprint with the host's administrator before "
- "connecting.\n\n"
- "Would you like to accept the host's key and connect anyway? "
- ).arg(mHost).arg(mKeyFingerprint);
- mConnectState = STATE_SEND_CONTINUE;
- return false;
-
- // STATE_DIFF_KEY_WAIT_CONTINUE:
- // Grab lines from ssh until we get a continue prompt or a auth
- // denied. We will get the later if StrictHostKeyChecking is set
- // to yes. Go to STATE_DIFF_KEY_CONTINUE if we get a continue prompt.
- case STATE_DIFF_KEY_WAIT_CONTINUE:
- line = getLine();
- if( line.isNull() ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- "Got null line in STATE_DIFF_KEY_WAIT_CONTINUE." << endl;
- mError = ERR_INTERACT;
- mErrorMsg =
- i18n("Error encountered while talking to ssh.");
- mConnectState = STATE_FATAL;
- }
- else if( (line.find(authFailedMsg[mVersion]) != -1
- && line.find(tryAgainMsg[mVersion]) == -1)
- || line.find(hostKeyVerifyFailedMsg[mVersion]) != -1 ) {
- mError = ERR_AUTH_FAILED_DIFF_KEY;
- mErrorMsg = i18n(
- "WARNING: The identity of the remote host '%1' has changed!\n\n"
- "Someone could be eavesdropping on your connection, or the "
- "administrator may have just changed the host's key. "
- "Either way, you should verify the host's key fingerprint with the host's "
- "administrator. The key fingerprint is:\n%2\n"
- "Add the correct host key to \"%3\" to "
- "get rid of this message."
- ).arg(mHost).arg(mKeyFingerprint).arg(mKnownHostsFile);
- mConnectState = STATE_FATAL;
- }
- else if( line.find(continuePrompt[mVersion]) != -1 ) {
- mConnectState = STATE_DIFF_KEY_CONTINUE;
- }
- else if( line.find(keyFingerprintMsg[mVersion]) != -1 ) {
- mKeyFingerprint = keyFingerprintMsg[mVersion].cap();
- kdDebug(KSSHPROC) << "Found key fingerprint: " << mKeyFingerprint << endl;
- mConnectState = STATE_DIFF_KEY_WAIT_CONTINUE;
- }
- else if( line.find(knownHostsFileMsg[mVersion]) != -1 ) {
- mKnownHostsFile = (knownHostsFileMsg[mVersion]).cap(1);
- kdDebug(KSSHPROC) << "Found known hosts file name: " << mKnownHostsFile << endl;
- mConnectState = STATE_DIFF_KEY_WAIT_CONTINUE;
- }
- else {
- // ignore line
- }
- break;
-
- // STATE_DIFF_KEY_CONTINUE:
- // We got a continue prompt for the different key message.
- // Set ERR_DIFF_HOST_KEY error
- // and return false to signal need to caller action.
- case STATE_DIFF_KEY_CONTINUE:
- mError = ERR_DIFF_HOST_KEY;
- mErrorMsg = i18n(
- "WARNING: The identity of the remote host '%1' has changed!\n\n"
- "Someone could be eavesdropping on your connection, or the "
- "administrator may have just changed the host's key. "
- "Either way, you should verify the host's key fingerprint with the host's "
- "administrator before connecting. The key fingerprint is:\n%2\n\n"
- "Would you like to accept the host's new key and connect anyway?"
- ).arg(mHost).arg(mKeyFingerprint);
- mConnectState = STATE_SEND_CONTINUE;
- return false;
-
- // STATE_SEND_CONTINUE:
- // We found a continue prompt. Send our answer.
- case STATE_SEND_CONTINUE:
- if( mAcceptHostKey ) {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- "host key accepted" << endl;
- ssh.writeLine("yes");
- mConnectState = STATE_WAIT_PROMPT;
- }
- else {
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- "host key rejected" << endl;
- ssh.writeLine("no");
- mError = ERR_HOST_KEY_REJECTED;
- mErrorMsg = i18n("Host key was rejected.");
- mConnectState = STATE_FATAL;
- }
- break;
-
- // STATE_FATAL:
- // Something bad happened that we cannot recover from.
- // Kill the ssh process and set flags to show we have
- // ended the connection and killed ssh.
- //
- // mError and mErrorMsg should be set by the immediately
- // previous state.
- case STATE_FATAL:
- kill();
- mConnected = false;
- mRunning = false;
- mConnectState = STATE_START;
- // mError, mErroMsg set by last state
- return false;
-
- default:
- kdDebug(KSSHPROC) << "KSshProcess::connect(): "
- "Invalid state number - " << mConnectState << endl;
- mError = ERR_INVALID_STATE;
- mConnectState = STATE_FATAL;
- }
- }
-
- // we should never get here
- kdDebug(KSSHPROC) << "KSshProcess::connect(): " <<
- "After switch(). We shouldn't be here." << endl;
- mError = ERR_INTERNAL;
- return false;
-}
-
-void KSshProcess::disconnect() {
- kill();
- mConnected = false;
- mRunning = false;
- mConnectState = STATE_START;
-}
-
diff --git a/tdeioslave/sftp/ksshprocess.h b/tdeioslave/sftp/ksshprocess.h
deleted file mode 100644
index 5130628e4..000000000
--- a/tdeioslave/sftp/ksshprocess.h
+++ /dev/null
@@ -1,623 +0,0 @@
-/***************************************************************************
- ksshprocess.h - description
- -------------------
- begin : Tue Jul 31 2001
- copyright : (C) 2001 by Lucas Fisher
- email : ljfisher@purdue.edu
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
-#ifndef KSSHPROCESS_H
-#define KSSHPROCESS_H
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <unistd.h>
-
-#include <tqvaluelist.h>
-
-#include <kdebug.h>
-
-#include "process.h"
-
-#define KSSHPROC 7120
-
-/**
- * Provides version independent access to ssh. Currently supported
- * versions of SSH are:
- * OpenSSH 2.9p1
- * OpenSSH 2.9p2
- * OpenSSH 3.0
- * OpenSSH 3.1
- * Commercial SSH 3.0.0
- * Other versions of OpenSSH and commerical SSH will probably work also.
- *
- * To setup a SSH connection first create a list of options to use and tell
- * KSshProcess about your options. Then start the ssh connection. Once the
- * connection is setup use the stdin, stdout, stderr, and pty file descriptors
- * to communicate with ssh. For a detailed example of how to use, see
- * ksshprocesstest.cpp.
- *
- * @author Lucas Fisher
- *
- * Example: Connect to ssh server on localhost
- * KSshProcess::SshOpt opt;
- * KSshProcess::SshOptList options;
- *
- * opt.opt = KSshProcess::SSH_HOST;
- * opt.str = "localhost";
- * options.append(opt);
- *
- * opt.opt = KSshProcess::SSH_USERNAME;
- * opt.str = "me";
- * options.append(opt);
- *
- * KSshProcess ssh;
- * if( !ssh.setOptions(options) ) {
- * int err = ssh.error();
- * // process error
- * return false;
- * }
- *
- * int err;
- * TQString errMsg;
- * while( !ssh.connect() ) {
- * err = ssh.error(errMsg);
- *
- * switch( err ) {
- * case KSshProcess::ERR_NEW_HOST_KEY:
- * case KSshProcess::ERR_DIFF_HOST_KEY:
- * // ask user to accept key
- * if( acceptHostKey ) {
- * ssh.acceptKey(true);
- * }
- * break;
- *
- * case KSshProcess::ERR_NEED_PASSWORD:
- * // ask user for password
- * ssh.password(userPassword);
- * break;
- *
- * case KSshProcess::ERR_NEED_KEY_PASSPHRASE:
- * // ask user for their key passphrase
- * ssh.keyPassphrase(keyPassphrase);
- * break;
- *
- * default:
- * // somethings wrong, alert user
- * return;
- * }
- * }
- * // We have an open ssh connection to localhost
- *
- */
-
-class KSshProcess {
-public:
- /**
- * SSH Option
- *
- * Stores SSH options for use with KSshProcess.
- *
- * SSH options are configured much like UDS entries.
- * Each option is assigned a constant and a string, bool,
- * or number is assigned based on the option.
- *
- * @author Lucas Fisher (ljfisher@iastate.edu)
- */
- class SshOpt {
- public:
- TQ_UINT32 opt;
- TQString str;
- TQ_INT32 num;
- bool boolean;
- };
-
- /**
- * List of SshOptions and associated iterators
- */
- typedef TQValueList<SshOpt> SshOptList;
- typedef TQValueListIterator<SshOpt> SshOptListIterator;
- typedef TQValueListConstIterator<SshOpt> SshOptListConstIterator;
-
- /**
- * Ssh versions supported by KSshProcess. Subject to change
- * at any time.
- */
- enum SshVersion {
- OPENSSH_3_6,
- OPENSSH,
- SSH,
- SSH_VER_MAX,
- UNKNOWN_VER
- };
-
- /**
- * SSH options supported by KSshProcess. Set SshOpt::opt to one of these
- * values.
- */
- // we cannot do this like UDSAtomType (ORing the type with the name) because
- // we have too many options for ssh and not enough bits.
- enum SshOptType {
- /**
- * Request server to invoke subsystem. (str)
- */
- SSH_SUBSYSTEM,
- /**
- * Connect to port on the server. (num)
- */
- SSH_PORT,
- /**
- * Connect to host. (str)
- */
- SSH_HOST,
- /**
- * connect using this username. (str)
- */
- SSH_USERNAME,
- /**
- * connect using this password. (str)
- */
- SSH_PASSWD,
- /**
- * connect using this version of the SSH protocol. num == 1 or 2
- */
- SSH_PROTOCOL,
- /**
- * whether to forward X11 connections. (boolean)
- */
- SSH_FORWARDX11,
- /**
- * whether to do agent forwarding. (boolean)
- */
- SSH_FORWARDAGENT,
- /**
- * use as escape character. 0 for none (num)
- */
- SSH_ESCAPE_CHAR,
- /**
- * command for ssh to perform once it is connected (str)
- */
- SSH_COMMAND,
- /**
- * Set ssh verbosity. This may be added multiple times. It may also cause KSSHProcess
- * to fail since we don't understand all the debug messages.
- */
- SSH_VERBOSE,
- /**
- * Set a ssh option as one would find in the ssh_config file
- * The str member should be set to 'optName value'
- */
- SSH_OPTION,
- /**
- * Set some other option not supported by KSSHProcess. The option should
- * be specified in the str member of SshOpt. Careful with this since
- * not all versions of SSH support the same options.
- */
- SSH_OTHER,
- SSH_OPT_MAX // always last
- }; // that's all for now
-
- /**
- * Errors that KSshProcess can encounter. When a member function returns
- * false, call error() to retrieve one of these error codes.
- */
- enum SshError {
- /**
- * Don't recognize the ssh version
- */
- ERR_UNKNOWN_VERSION,
- /**
- * Cannot lauch ssh client
- */
- ERR_CANNOT_LAUNCH,
- /**
- * Interaction with the ssh client failed. This happens when we can't
- * find the password prompt or something similar
- */
- ERR_INTERACT,
- /**
- * Arguments for both a remotely executed subsystem and command were provide.
- * Only one or the other may be used
- */
- ERR_CMD_SUBSYS_CONFLICT,
- /**
- * No password was supplied
- */
- ERR_NEED_PASSWD,
- /**
- * No passphrase was supplied.
- */
- ERR_NEED_PASSPHRASE,
- /**
- * No usename was supplied
- */
- ERR_NEED_USERNAME,
- /**
- * Timed out waiting for a response from ssh or the server
- */
- ERR_TIMED_OUT,
- /**
- * Internal error, probably from a system call
- */
- ERR_INTERNAL,
- /**
- * ssh was disconnect from the host
- */
- ERR_DISCONNECTED,
- /**
- * No ssh options have been set. Call setArgs() before calling connect.
- */
- ERR_NO_OPTIONS,
- /**
- * A host key was received from an unknown host.
- * Call connect() with the acceptHostKey argument to accept the key.
- */
- ERR_NEW_HOST_KEY,
- /**
- * A host key different from what is stored in the user's known_hosts file
- * has be received. This is an indication of an attack
- */
- ERR_DIFF_HOST_KEY,
- /**
- * A new or different host key was rejected by the caller. The ssh
- * connection was terminated and the ssh process killed.
- */
- ERR_HOST_KEY_REJECTED,
- /**
- * An invalid option was found in the SSH option list
- */
- ERR_INVALID_OPT,
- /**
- * SSH accepted host key without prompting user.
- */
- ERR_ACCEPTED_KEY,
- /**
- * Authentication failed
- */
- ERR_AUTH_FAILED,
- /**
- * Authentication failed because a new host key was detected and
- * SSH is configured with strict host key checking enabled.
- */
- ERR_AUTH_FAILED_NEW_KEY,
- /**
- * Authentication failed because a changed host key was detected and
- * SSH is configured with strict host key checking enabled.
- */
- ERR_AUTH_FAILED_DIFF_KEY,
- /**
- * The remote host closed the connection for unknown reasons.
- */
- ERR_CLOSED_BY_REMOTE_HOST,
- /**
- * We have no idea what happened
- */
- ERR_UNKNOWN,
- /**
- * The connect state machine entered an invalid state.
- */
- ERR_INVALID_STATE,
- ERR_MAX
- };
-
- /**
- * Initialize a SSH process using the first SSH binary found in the PATH
- */
- KSshProcess();
-
- /**
- * Initialize a SSH process using the specified SSH binary.
- * @param pathToSsh The fully qualified path name of the ssh binary
- * KSshProcess should use to setup a SSH connection.
- */
- KSshProcess(TQString pathToSsh);
- ~KSshProcess();
-
- /**
- * Set the ssh binary KSshProcess should use. This will only affect the
- * next ssh connection attempt using this instance.
- *
- * @param pathToSsh Full path to the ssh binary.
- *
- * @return True if the ssh binary is found and KSshProcess
- * recognizes the version.
- *
- */
- bool setSshPath(TQString pathToSsh);
-
- /**
- * Get the ssh version.
- *
- * @return The ssh version or -1 if KSshProcess does not recognize
- * the ssh version. The returned value corresponds to the
- * member of the SshVersion enum.
- */
- SshVersion version();
-
- /**
- * Get a string describing the ssh version
- *
- * @return A string describing the ssh version recognized by KSshProcess
- */
- //TQString versionStr();
-
- /**
- * Get the last error encountered by KSshProcess.
- *
- * @param msg Set to the error message, if any, outputted by ssh when it is run.
- *
- * @return The error number. See SshError for descriptions.
- */
- int error(TQString& msg);
-
- /**
- * Get the last error encountered by KSshProcess.
- * @return The error number. See SshError for descriptions.
- */
- int error() { return mError; }
-
- TQString errorMsg() { return mErrorMsg; }
-
- /**
- * Send a signal to the ssh process. Do not use this to end the
- * ssh connection as it will not correctly reset the internal
- * state of the KSshProcess object. Use KSshProcess::disconnect()
- * instead.
- *
- * @param signal The signal to send to the ssh process. See 'kill -l'
- * for a list of possible signals.
- * The default signal is SIGKILL which kills ssh.
- *
- */
- void kill(int signal = SIGKILL);
-
- /**
- * The pid of the ssh process started by this instance of KSshProcess.
- * Only valid if KSshProcess::running() returns true;
- *
- * @return The pid of the running ssh process.
- */
- int pid() { return ssh.pid(); }
-
- /**
- * Whether a ssh connection has been established with a
- * remote host. A establish connection means ssh has successfully
- * authenticated with the remote host and user data can be transfered
- * between the local and remote host. This cannot return
- * true unless the most recent call to KSshProccess::connect() returned true.
- *
- * @return True if a ssh connection has been established with a remote
- * host. False otherwise.
- */
- bool connected() { return mConnected; }
-
- /**
- * Whether a ssh process is currently running. This only indicates
- * if a ssh process has been started and is still running. It does not
- * tell if authentication has been successful. This may return true
- * even if the most recent call to KSshProcess::connect() returned false.
- *
- * @return True if a ssh process started by this instance of KSshProcess
- * is running. False otherwise.
- */
- bool running() { return mRunning; }
-
- /**
- * Print the command line arguments ssh is run with using kdDebug.
- */
- void printArgs();
-
- /**
- * Set the SSH options.
- * This must be called before connect(). See SshOptType for a list of
- * supported ssh options. The required options are SSH_USERNAME
- * and SSH_HOST.
- *
- * To reset the saved options, just recall setOptions() again with
- * a different options list.
- *
- * @param opts A list of SshOpt objects specifying the ssh options.
- *
- * @return True if all options are valid. False if unrecognized options
- * or a required option is missing. Call error()
- * for details.
- *
- */
- bool setOptions(const SshOptList& opts);
-
- /**
- * Create a ssh connection based on the options provided by setOptions().
- * Sets one of the following error codes on failure:
- * <ul>
- * <li>ERR_NO_OPTIONS</li>
- * <li>ERR_CANNOT_LAUNCH</li>
- * <li>ERR_INVALID_STATE</li>
- * <li>ERR_NEED_PASSWD</li>
- * <li>ERR_AUTH_FAILED</li>
- * <li>ERR_NEW_HOST_KEY</li>
- * <li>ERR_KEY_ACCEPTED</li>
- * <li>ERR_DIFF_HOST_KEY</li>
- * <li>ERR_INTERNAL</li>
- * <li>ERR_INTERACT</li>
- * </ul>
- *
- * @param acceptHostKey When true KSshProcess will automatically accept
- * unrecognized or changed host keys.
- *
- * @return True if the ssh connection is successful. False if the connection
- * fails. Call error() to get the reason for the failure.
- */
- bool connect();
-
-
- /**
- * Disconnect ssh from the host. This kills the ssh process and
- * resets the internal state of this KSshProcess object. After a
- * disconnect, the same KSshProcess can be used to connect to a
- * host.
- */
- void disconnect();
-
- /**
- * Call to respond to a ERR_NEW_HOST_KEY or ERR_DIFF_HOST_KEY error.
- *
- * @param accept True to accept the host key, false to not accept the
- * host key and kill ssh.
- *
- */
- void acceptHostKey(bool accept);
-
- /**
- * Call to respond to a ERR_NEED_PASSWD or ERR_NEED_PASSPHRASE error.
- *
- * @param password The user password to give ssh.
- */
- void setPassword(TQString password);
-
- /**
- * Access to standard in and out of the ssh process.
- *
- * @return The file description for stdin and stdout of the ssh process.
- */
- int stdioFd() { return ssh.stdioFd(); }
-
- /**
- * Access to standard error of the ssh process.
- *
- * @return The file descriptior for stderr of the ssh process.
- */
- int stderrFd() { return ssh.stderrFd(); }
-
- /**
- * Access the pty to which the ssh process is attached.
- *
- * @return The file descriptor of pty to which ssh is attached.
- */
- int pty() { return ssh.fd(); }
-private:
- /**
- * Path the the ssh binary.
- */
- TQString mSshPath;
-
- /**
- * SSH version. This is an index into the supported SSH
- * versions array, and the various messages arrays.
- */
- SshVersion mVersion;
-
- /**
- * User's password. Zero this out when it is no longer needed.
- */
- TQString mPassword;
-
- /**
- * User's username.
- */
- TQString mUsername;
-
- /**
- * Name of host we are connecting to.
- */
- TQString mHost;
-
- /**
- * Accept new or changed host keys if true.
- */
- bool mAcceptHostKey;
-
- /**
- * Flag to tell use if we have an open, authenticated ssh
- * session going.
- */
- bool mConnected;
-
- /**
- * Flag to tell us if we have started a ssh process, we use this
- * to make sure we kill ssh before going away.
- */
- bool mRunning;
-
- /**
- * Save any key fingerprint msg from ssh so we can present
- * it to the caller.
- */
- TQString mKeyFingerprint;
-
- /**
- * The location of the known host key file. We grab this from
- * any error messages ssh prints out.
- */
- TQString mKnownHostsFile;
-
- /**
- * The state of our connect state machine.
- */
- int mConnectState;
-
- /**
- * Port on on which the target ssh server is listening.
- */
- int mPort;
-
- /**
- * The last error number encountered. This is only valid for the
- * last error.
- */
- SshError mError;
-
- /**
- * An error message that corresponds to the error number set in
- * mError. Optional.
- */
- TQString mErrorMsg;
-
- /**
- * Interface to the SSH process we ceate. Handles communication
- * to and from the SSH process using stdin, stdout, stderr, and
- * pty.
- */
- MyPtyProcess ssh;
-
- /**
- * List of arguments we start SSH with.
- */
- QCStringList mArgs;
- void init();
-
- /**
- * Handler to clean up when ssh process terminates.
- */
- static void SIGCHLD_handler(int signo);
- void installSignalHandlers();
- void removeSignalHandlers();
-
- TQString getLine();
-
- static TQRegExp versionStrs[];
- static const char * const passwordPrompt[];
- static const char * const passphrasePrompt[];
- static const char * const authSuccessMsg[];
- static const char * const authFailedMsg[];
- static TQRegExp hostKeyMissingMsg[];
- static const char * const hostKeyChangedMsg[];
- static const char * const continuePrompt[];
- static const char * const hostKeyAcceptedMsg[];
- static const char * const tryAgainMsg[];
- static TQRegExp hostKeyVerifyFailedMsg[];
- static const char * const connectionClosedMsg[];
- static const char * const changeHostKeyOnDiskPrompt[];
- static TQRegExp keyFingerprintMsg[];
- static TQRegExp knownHostsFileMsg[];
-};
-#endif
diff --git a/tdeioslave/sftp/ksshprocesstest.cpp b/tdeioslave/sftp/ksshprocesstest.cpp
deleted file mode 100644
index 59dbf58c7..000000000
--- a/tdeioslave/sftp/ksshprocesstest.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#include "ksshprocess.h"
-#include <iostream>
-
-using namespace std;
-
-int main(int argc, char *argv[]) {
-
- if( argc < 5 ) {
- cout << "Usage: " << argv[0] <<
- " <ssh path> <host> <username> <password>" << endl;
- return 1;
- }
-
- KSshProcess ssh(argv[1]);
- cout << ssh.version() << endl;
-
- KSshProcess::SshOptList opts;
- KSshProcess::SshOpt opt;
-
- opt.opt = KSshProcess::SSH_PORT;
- opt.num = 22;
- opts.append(opt);
-
- opt.opt = KSshProcess::SSH_HOST;
- opt.str = TQString(argv[2]);
- opts.append(opt);
-
- opt.opt = KSshProcess::SSH_USERNAME;
- opt.str = TQString(argv[3]);
- opts.append(opt);
-
-// opt.opt = KSshProcess::SSH_PASSWD;
-// opt.str = TQString(argv[4]);
-// opts.append(opt);
-
- if( !ssh.setOptions(opts) ) {
- cout << "ksshprocesstest: setOptions failed" << endl;
- return -1;
- }
-
- ssh.printArgs();
-
- bool stop = false;
- bool connected;
- char buf[256];
- char c;
- while( !stop && !(connected = ssh.connect()) ) {
- cout << "ksshprocesstest: Error num - " << ssh.error() << endl;
- cout << "ksshprocesstest: Error msg - " << ssh.errorMsg().latin1() << endl;
- switch( ssh.error() ) {
- case KSshProcess::ERR_NEED_PASSWD:
- case KSshProcess::ERR_NEED_PASSPHRASE:
- cout << "Password: ";
- cin >> buf;
- cout << "password is " << buf << endl;
- ssh.setPassword(TQString(buf));
- break;
- case KSshProcess::ERR_NEW_HOST_KEY:
- case KSshProcess::ERR_DIFF_HOST_KEY:
- cout << "Accept host key? (y/n): ";
- cin >> c;
- cout << "Answered " << c << endl;
- ssh.acceptHostKey(c == 'y' ? true : false);
- break;
- case KSshProcess::ERR_AUTH_FAILED:
- cout << "ksshprocesstest: auth failed." << endl;
- stop = true;
- break;
- case KSshProcess::ERR_AUTH_FAILED_NEW_KEY:
- cout << "ksshprocesstest: auth failed because of new key." << endl;
- stop = true;
- break;
- case KSshProcess::ERR_AUTH_FAILED_DIFF_KEY:
- cout << "ksshprocesstest: auth failed because of changed key." << endl;
- stop = true;
- break;
-
- case KSshProcess::ERR_INTERACT:
- case KSshProcess::ERR_INTERNAL:
- case KSshProcess::ERR_UNKNOWN:
- case KSshProcess::ERR_INVALID_STATE:
- case KSshProcess::ERR_CANNOT_LAUNCH:
- case KSshProcess::ERR_HOST_KEY_REJECTED:
- cout << "ksshprocesstest: FATAL ERROR" << endl;
- stop = true;
- break;
-
- }
- }
-
- if( connected ) {
- cout << "ksshprocesstest: Successfully connected to " << argv[2] << endl;
- }
- else {
- cout << "ksshprocesstest: Connect to " << argv[2] << " failed." << endl;
- }
-
-}
diff --git a/tdeioslave/sftp/process.cpp b/tdeioslave/sftp/process.cpp
deleted file mode 100644
index 164121497..000000000
--- a/tdeioslave/sftp/process.cpp
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- *
- *
- * This file is part of the KDE project, module tdesu.
- * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
- *
- * This file contains code from TEShell.C of the KDE konsole.
- * Copyright (c) 1997,1998 by Lars Doelle <lars.doelle@on-line.de>
- *
- * This is free software; you can use this library under the GNU Library
- * General Public License, version 2. See the file "COPYING.LIB" for the
- * exact licensing terms.
- *
- * process.cpp: Functionality to build a front end to password asking
- * terminal programs.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <errno.h>
-#include <string.h>
-#include <termios.h>
-#include <signal.h>
-
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-
-#if defined(__SVR4) && defined(sun)
-#include <stropts.h>
-#include <sys/stream.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h> // Needed on some systems.
-#endif
-
-#include <tqglobal.h>
-#include <tqcstring.h>
-#include <tqfile.h>
-
-#include <kdebug.h>
-#include <kstandarddirs.h>
-
-#include "process.h"
-#include <tdesu/tdesu_pty.h>
-#include <tdesu/kcookie.h>
-
-
-MyPtyProcess::MyPtyProcess()
-{
- m_bTerminal = false;
- m_bErase = false;
- m_pPTY = 0L;
- m_Pid = -1;
- m_Fd = -1;
-}
-
-
-int MyPtyProcess::init()
-{
- delete m_pPTY;
- m_pPTY = new PTY();
- m_Fd = m_pPTY->getpt();
- if (m_Fd < 0)
- return -1;
- if ((m_pPTY->grantpt() < 0) || (m_pPTY->unlockpt() < 0))
- {
- kdError(PTYPROC) << k_lineinfo << "Master setup failed.\n" << endl;
- m_Fd = -1;
- return -1;
- }
- m_TTY = m_pPTY->ptsname();
- m_stdoutBuf.resize(0);
- m_stderrBuf.resize(0);
- m_ptyBuf.resize(0);
- return 0;
-}
-
-
-MyPtyProcess::~MyPtyProcess()
-{
- delete m_pPTY;
-}
-
-
-/*
- * Read one line of input. The terminal is in canonical mode, so you always
- * read a line at at time, but it's possible to receive multiple lines in
- * one time.
- */
-
-
-TQCString MyPtyProcess::readLineFrom(int fd, TQCString& inbuf, bool block)
-{
- int pos;
- TQCString ret;
-
- if (!inbuf.isEmpty())
- {
-
- pos = inbuf.find('\n');
-
- if (pos == -1)
- {
- ret = inbuf;
- inbuf.resize(0);
- } else
- {
- ret = inbuf.left(pos);
- inbuf = inbuf.mid(pos+1);
- }
- return ret;
-
- }
-
- int flags = fcntl(fd, F_GETFL);
- if (flags < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "fcntl(F_GETFL): " << perror << "\n";
- return ret;
- }
- if (block)
- flags &= ~O_NONBLOCK;
- else
- flags |= O_NONBLOCK;
- if (fcntl(fd, F_SETFL, flags) < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "fcntl(F_SETFL): " << perror << "\n";
- return ret;
- }
-
- int nbytes;
- char buf[256];
- while (1)
- {
- nbytes = read(fd, buf, 255);
- if (nbytes == -1)
- {
- if (errno == EINTR)
- continue;
- else break;
- }
- if (nbytes == 0)
- break; // eof
-
- buf[nbytes] = '\000';
- inbuf += buf;
-
- pos = inbuf.find('\n');
- if (pos == -1)
- {
- ret = inbuf;
- inbuf.resize(0);
- } else
- {
- ret = inbuf.left(pos);
- inbuf = inbuf.mid(pos+1);
- }
- break;
-
- }
-
- return ret;
-}
-
-void MyPtyProcess::writeLine(TQCString line, bool addnl)
-{
- if (!line.isEmpty())
- write(m_Fd, line, line.length());
- if (addnl)
- write(m_Fd, "\n", 1);
-}
-
-void MyPtyProcess::unreadLineFrom(TQCString inbuf, TQCString line, bool addnl)
-{
- if (addnl)
- line += '\n';
- if (!line.isEmpty())
- inbuf.prepend(line);
-}
-
-
-/*
- * Fork and execute the command. This returns in the parent.
- */
-
-int MyPtyProcess::exec(TQCString command, QCStringList args)
-{
- kdDebug(PTYPROC) << "MyPtyProcess::exec(): " << command << endl;// << ", args = " << args << endl;
-
- if (init() < 0)
- return -1;
-
- // Open the pty slave before forking. See SetupTTY()
- int slave = open(m_TTY, O_RDWR);
- if (slave < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "Could not open slave pty.\n";
- return -1;
- }
-
- // Also create a socket pair to connect to standard in/out.
- // This will allow use to bypass the terminal.
- int inout[2];
- int err[2];
- int ok = 1;
- ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, inout) >= 0;
- ok &= socketpair(AF_UNIX, SOCK_STREAM, 0, err ) >= 0;
- if( !ok ) {
- kdDebug(PTYPROC) << "Could not create socket" << endl;
- return -1;
- }
- m_stdinout = inout[0];
- m_err = err[0];
-
- if ((m_Pid = fork()) == -1)
- {
- kdError(PTYPROC) << k_lineinfo << "fork(): " << perror << "\n";
- return -1;
- }
-
- // Parent
- if (m_Pid)
- {
- close(slave);
- close(inout[1]);
- close(err[1]);
- return 0;
- }
-
- // Child
-
- ok = 1;
- ok &= dup2(inout[1], STDIN_FILENO) >= 0;
- ok &= dup2(inout[1], STDOUT_FILENO) >= 0;
- ok &= dup2(err[1], STDERR_FILENO) >= 0;
-
- if( !ok )
- {
- kdError(PTYPROC) << "dup of socket descriptor failed" << endl;
- _exit(1);
- }
-
- close(inout[1]);
- close(inout[0]);
- close(err[1]);
- close(err[0]);
-
- if (SetupTTY(slave) < 0)
- _exit(1);
-
- // From now on, terminal output goes through the tty.
- TQCString path;
- if (command.contains('/'))
- path = command;
- else
- {
- TQString file = TDEStandardDirs::findExe(command);
- if (file.isEmpty())
- {
- kdError(PTYPROC) << k_lineinfo << command << " not found\n";
- _exit(1);
- }
- path = TQFile::encodeName(file);
- }
-
- int i;
- const char * argp[32];
- argp[0] = path;
- QCStringList::Iterator it;
- for (i=1, it=args.begin(); it!=args.end() && i<31; it++) {
- argp[i++] = *it;
- kdDebug(PTYPROC) << *it << endl;
- }
- argp[i] = 0L;
- execv(path, (char * const *)argp);
- kdError(PTYPROC) << k_lineinfo << "execv(\"" << path << "\"): " << perror << "\n";
- _exit(1);
- return -1; // Shut up compiler. Never reached.
-}
-
-/*
- * Wait until the terminal is set into no echo mode. At least one su
- * (RH6 w/ Linux-PAM patches) sets noecho mode AFTER writing the Password:
- * prompt, using TCSAFLUSH. This flushes the terminal I/O queues, possibly
- * taking the password with it. So we wait until no echo mode is set
- * before writing the password.
- * Note that this is done on the slave fd. While Linux allows tcgetattr() on
- * the master side, Solaris doesn't.
- */
-
-int MyPtyProcess::WaitSlave()
-{
- int slave = open(m_TTY, O_RDWR);
- if (slave < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "Could not open slave tty.\n";
- return -1;
- }
-
- struct termios tio;
- struct timeval tv;
- while (1)
- {
- if (tcgetattr(slave, &tio) < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "tcgetattr(): " << perror << "\n";
- close(slave);
- return -1;
- }
- if (tio.c_lflag & ECHO)
- {
- kdDebug(PTYPROC) << k_lineinfo << "Echo mode still on." << endl;
- // sleep 1/10 sec
- tv.tv_sec = 0; tv.tv_usec = 100000;
- select(slave, 0L, 0L, 0L, &tv);
- continue;
- }
- break;
- }
- close(slave);
- return 0;
-}
-
-
-int MyPtyProcess::enableLocalEcho(bool enable)
-{
- int slave = open(m_TTY, O_RDWR);
- if (slave < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "Could not open slave tty.\n";
- return -1;
- }
- struct termios tio;
- if (tcgetattr(slave, &tio) < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "tcgetattr(): " << perror << "\n";
- close(slave); return -1;
- }
- if (enable)
- tio.c_lflag |= ECHO;
- else
- tio.c_lflag &= ~ECHO;
- if (tcsetattr(slave, TCSANOW, &tio) < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "tcsetattr(): " << perror << "\n";
- close(slave); return -1;
- }
- close(slave);
- return 0;
-}
-
-
-/*
- * Copy output to stdout until the child process exists, or a line of output
- * matches `m_Exit'.
- * We have to use waitpid() to test for exit. Merely waiting for EOF on the
- * pty does not work, because the target process may have children still
- * attached to the terminal.
- */
-
-int MyPtyProcess::waitForChild()
-{
- int ret, state, retval = 1;
- struct timeval tv;
-
- fd_set fds;
- FD_ZERO(&fds);
-
- while (1)
- {
- tv.tv_sec = 1; tv.tv_usec = 0;
- FD_SET(m_Fd, &fds);
- ret = select(m_Fd+1, &fds, 0L, 0L, &tv);
- if (ret == -1)
- {
- if (errno == EINTR) continue;
- else
- {
- kdError(PTYPROC) << k_lineinfo << "select(): " << perror << "\n";
- return -1;
- }
- }
-
- if (ret)
- {
- TQCString line = readLine(false);
- while (!line.isNull())
- {
- if (!m_Exit.isEmpty() && !tqstrnicmp(line, m_Exit, m_Exit.length()))
- kill(m_Pid, SIGTERM);
- if (m_bTerminal)
- {
- fputs(line, stdout);
- fputc('\n', stdout);
- }
- line = readLine(false);
- }
- }
-
- // Check if the process is still alive
- ret = waitpid(m_Pid, &state, WNOHANG);
- if (ret < 0)
- {
- if (errno == ECHILD)
- retval = 0;
- else
- kdError(PTYPROC) << k_lineinfo << "waitpid(): " << perror << "\n";
- break;
- }
- if (ret == m_Pid)
- {
- if (WIFEXITED(state))
- retval = WEXITSTATUS(state);
- break;
- }
- }
-
- return -retval;
-}
-
-/*
- * SetupTTY: Creates a new session. The filedescriptor "fd" should be
- * connected to the tty. It is closed after the tty is reopened to make it
- * our controlling terminal. This way the tty is always opened at least once
- * so we'll never get EIO when reading from it.
- */
-
-int MyPtyProcess::SetupTTY(int fd)
-{
- // Reset signal handlers
- for (int sig = 1; sig < NSIG; sig++)
- signal(sig, SIG_DFL);
- signal(SIGHUP, SIG_IGN);
-
- // Close all file handles
-// struct rlimit rlp;
-// getrlimit(RLIMIT_NOFILE, &rlp);
-// for (int i = 0; i < (int)rlp.rlim_cur; i++)
-// if (i != fd) close(i);
-
- // Create a new session.
- setsid();
-
- // Open slave. This will make it our controlling terminal
- int slave = open(m_TTY, O_RDWR);
- if (slave < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "Could not open slave side: " << perror << "\n";
- return -1;
- }
- close(fd);
-
-#if defined(__SVR4) && defined(sun)
-
- // Solaris STREAMS environment.
- // Push these modules to make the stream look like a terminal.
- ioctl(slave, I_PUSH, "ptem");
- ioctl(slave, I_PUSH, "ldterm");
-
-#endif
-
- // Connect stdin, stdout and stderr
-// dup2(slave, 0); dup2(slave, 1); dup2(slave, 2);
-// if (slave > 2)
-// close(slave);
-
- // Disable OPOST processing. Otherwise, '\n' are (on Linux at least)
- // translated to '\r\n'.
- struct termios tio;
- if (tcgetattr(slave, &tio) < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "tcgetattr(): " << perror << "\n";
- return -1;
- }
- tio.c_oflag &= ~OPOST;
- if (tcsetattr(slave, TCSANOW, &tio) < 0)
- {
- kdError(PTYPROC) << k_lineinfo << "tcsetattr(): " << perror << "\n";
- return -1;
- }
-
- return 0;
-}
diff --git a/tdeioslave/sftp/process.h b/tdeioslave/sftp/process.h
deleted file mode 100644
index ca154f45c..000000000
--- a/tdeioslave/sftp/process.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- *
- *
- * This file is part of the KDE project, module tdesu.
- * Copyright (C) 1999,2000 Geert Jansen <jansen@kde.org>
- *
- * This is free software; you can use this library under the GNU Library
- * General Public License, version 2. See the file "COPYING.LIB" for the
- * exact licensing terms.
- */
-
-#ifndef __Process_h_Included__
-#define __Process_h_Included__
-
-#include <tqcstring.h>
-#include <tqstring.h>
-#include <tqstringlist.h>
-#include <tqvaluelist.h>
-
-#define PTYPROC 7120
-
-class PTY;
-typedef TQValueList<TQCString> QCStringList;
-
-/**
- * Synchronous communication with tty programs.
- *
- * PtyProcess provides synchronous communication with tty based programs.
- * The communications channel used is a pseudo tty (as opposed to a pipe)
- * This means that programs which require a terminal will work.
- */
-
-class MyPtyProcess
-{
-public:
- MyPtyProcess();
- virtual ~MyPtyProcess();
-
- /**
- * Fork off and execute a command. The command's standard in and output
- * are connected to the pseudo tty. They are accessible with @ref #readLine
- * and @ref #writeLine.
- * @param command The command to execute.
- * @param args The arguments to the command.
- */
- int exec(TQCString command, QCStringList args);
-
- /**
- * Read a line from the program's standard out. Depending on the @em block
- * parameter, this call blocks until a single, full line is read.
- * @param block Block until a full line is read?
- * @return The output string.
- */
- TQCString readLine(bool block = true)
- { return readLineFrom(m_Fd, m_ptyBuf, block); }
-
- TQCString readLineFromPty(bool block = true)
- { return readLineFrom(m_Fd, m_ptyBuf, block); }
-
- TQCString readLineFromStdout(bool block = true)
- { return readLineFrom(m_stdinout, m_stdoutBuf, block); }
-
- TQCString readLineFromStderr(bool block = true)
- { return readLineFrom(m_err, m_stderrBuf, block); }
-
- /**
- * Write a line of text to the program's standard in.
- * @param line The text to write.
- * @param addNewline Adds a '\n' to the line.
- */
- void writeLine(TQCString line, bool addNewline=true);
-
- /**
- * Put back a line of input.
- * @param line The line to put back.
- * @param addNewline Adds a '\n' to the line.
- */
-
- void unreadLine(TQCString line, bool addNewline = true)
- { unreadLineFrom(m_ptyBuf, line, addNewline); }
-
- void unreadLineFromPty(TQCString line, bool addNewline = true)
- { unreadLineFrom(m_ptyBuf, line, addNewline); }
-
- void unreadLineFromStderr(TQCString line, bool addNewline = true)
- { unreadLineFrom(m_stderrBuf, line, addNewline); }
-
- void unreadLineFromStdout(TQCString line, bool addNewline = true)
- { unreadLineFrom(m_stdoutBuf, line, addNewline); }
-
- /**
- * Set exit string. If a line of program output matches this,
- * @ref #waitForChild() will terminate the program and return.
- */
- void setExitString(TQCString exit) { m_Exit = exit; }
-
- /**
- * Wait for the child to exit. See also @ref #setExitString.
- */
- int waitForChild();
-
- /**
- * Wait until the pty has cleared the ECHO flag. This is useful
- * when programs write a password prompt before they disable ECHO.
- * Disabling it might flush any input that was written.
- */
- int WaitSlave();
-
- /** Enables/disables local echo on the pseudo tty. */
- int enableLocalEcho(bool enable=true);
-
- /** Enable/disable terminal output. Relevant only to some subclasses. */
- void setTerminal(bool terminal) { m_bTerminal = terminal; }
-
- /** Overwritte the password as soon as it is used. Relevant only to
- * some subclasses. */
- void setErase(bool erase) { m_bErase = erase; }
-
- /** Return the filedescriptor of the process. */
- int fd() {return m_Fd;}
-
- /** Return the pid of the process. */
- int pid() {return m_Pid;}
-
- int stdioFd() {return m_stdinout;}
-
- int stderrFd() {return m_err;}
-
-protected:
- bool m_bErase, m_bTerminal;
- int m_Pid, m_Fd, m_stdinout, m_err;
- TQCString m_Command, m_Exit;
-
-private:
- int init();
- int SetupTTY(int fd);
-
- PTY *m_pPTY;
- TQCString m_TTY;
- TQCString m_ptyBuf, m_stderrBuf, m_stdoutBuf;
-
- TQCString readLineFrom(int fd, TQCString& inbuf, bool block);
- void unreadLineFrom(TQCString inbuf, TQCString line, bool addnl);
- class PtyProcessPrivate;
- PtyProcessPrivate *d;
-};
-
-#endif
diff --git a/tdeioslave/sftp/sftp.h b/tdeioslave/sftp/sftp.h
deleted file mode 100644
index 95518130d..000000000
--- a/tdeioslave/sftp/sftp.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/* $OpenBSD: sftp.h,v 1.3 2001/03/07 10:11:23 djm Exp $ */
-
-/*
- * Copyright (c) 2001 Markus Friedl. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/*
- * draft-ietf-secsh-filexfer-01.txt
- */
-
-/* version */
-#define SSH2_FILEXFER_VERSION 3
-
-/* client to server */
-#define SSH2_FXP_INIT 1
-#define SSH2_FXP_OPEN 3
-#define SSH2_FXP_CLOSE 4
-#define SSH2_FXP_READ 5
-#define SSH2_FXP_WRITE 6
-#define SSH2_FXP_LSTAT 7
-#define SSH2_FXP_FSTAT 8
-#define SSH2_FXP_SETSTAT 9
-#define SSH2_FXP_FSETSTAT 10
-#define SSH2_FXP_OPENDIR 11
-#define SSH2_FXP_READDIR 12
-#define SSH2_FXP_REMOVE 13
-#define SSH2_FXP_MKDIR 14
-#define SSH2_FXP_RMDIR 15
-#define SSH2_FXP_REALPATH 16
-#define SSH2_FXP_STAT 17
-#define SSH2_FXP_RENAME 18
-#define SSH2_FXP_READLINK 19
-#define SSH2_FXP_SYMLINK 20
-
-/* server to client */
-#define SSH2_FXP_VERSION 2
-#define SSH2_FXP_STATUS 101
-#define SSH2_FXP_HANDLE 102
-#define SSH2_FXP_DATA 103
-#define SSH2_FXP_NAME 104
-#define SSH2_FXP_ATTRS 105
-
-#define SSH2_FXP_EXTENDED 200
-#define SSH2_FXP_EXTENDED_REPLY 201
-
-/* attributes */
-#define SSH2_FILEXFER_ATTR_SIZE 0x00000001
-#define SSH2_FILEXFER_ATTR_UIDGID 0x00000002
-#define SSH2_FILEXFER_ATTR_PERMISSIONS 0x00000004
-#define SSH2_FILEXFER_ATTR_ACMODTIME 0x00000008
-#define SSH2_FILEXFER_ATTR_EXTENDED 0x80000000
-
-/* portable open modes */
-#define SSH2_FXF_READ 0x00000001
-#define SSH2_FXF_WRITE 0x00000002
-#define SSH2_FXF_APPEND 0x00000004
-#define SSH2_FXF_CREAT 0x00000008
-#define SSH2_FXF_TRUNC 0x00000010
-#define SSH2_FXF_EXCL 0x00000020
-
-/* status messages */
-#define SSH2_FX_OK 0
-#define SSH2_FX_EOF 1
-#define SSH2_FX_NO_SUCH_FILE 2
-#define SSH2_FX_PERMISSION_DENIED 3
-#define SSH2_FX_FAILURE 4
-#define SSH2_FX_BAD_MESSAGE 5
-#define SSH2_FX_NO_CONNECTION 6
-#define SSH2_FX_CONNECTION_LOST 7
-#define SSH2_FX_OP_UNSUPPORTED 8
-#define SSH2_FX_MAX 8
diff --git a/tdeioslave/sftp/sftp.protocol b/tdeioslave/sftp/sftp.protocol
index 33e66867a..a317beb25 100644
--- a/tdeioslave/sftp/sftp.protocol
+++ b/tdeioslave/sftp/sftp.protocol
@@ -2,7 +2,7 @@
exec=tdeio_sftp
protocol=sftp
input=none
-listing=Name,Type,Size,Date,Access,Owner,Group,Link
+listing=Name,Type,Size,Date,Access,Owner,Group,Link,MimeType
output=filesystem
copyToFile=true
copyFromFile=true
@@ -12,7 +12,7 @@ makedir=true
deleting=true
moving=true
Icon=ftp
-Description=A tdeioslave for sftp
+Description=A new tdeioslave for sftp
X-DocPath=tdeioslave/sftp/index.html
Icon=ftp
Class=:internet
diff --git a/tdeioslave/sftp/sftpfileattr.cpp b/tdeioslave/sftp/sftpfileattr.cpp
deleted file mode 100644
index ddd94bbe8..000000000
--- a/tdeioslave/sftp/sftpfileattr.cpp
+++ /dev/null
@@ -1,345 +0,0 @@
-/***************************************************************************
- sftpfileattr.cpp - description
- -------------------
- begin : Sat Jun 30 2001
- copyright : (C) 2001 by Lucas Fisher
- email : ljfisher@iastate.edu
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
-#include "sftpfileattr.h"
-
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <tqstring.h>
-#include <tqdatastream.h>
-
-#include <tdeio/global.h>
-#include <kremoteencoding.h>
-
-using namespace TDEIO;
-
-sftpFileAttr::sftpFileAttr(){
- clear();
- mDirAttrs = false;
-}
-
-sftpFileAttr::sftpFileAttr(KRemoteEncoding* encoding){
- clear();
- mDirAttrs = false;
- mEncoding = encoding;
-}
-
-
-/** Constructor to initialize the file attributes on declaration. */
-sftpFileAttr::sftpFileAttr(TQ_ULLONG size, uid_t uid, gid_t gid,
- mode_t permissions, time_t atime,
- time_t mtime, TQ_UINT32 extendedCount) {
- clear();
- mDirAttrs = false;
- mSize = size;
- mUid = uid;
- mGid = gid;
- mAtime = atime;
- mMtime = mtime;
- mPermissions = permissions;
- mExtendedCount = extendedCount;
-}
-
-sftpFileAttr::~sftpFileAttr(){
-}
-
-/** Returns a UDSEntry describing the file.
-The UDSEntry is generated from the sftp file attributes. */
-UDSEntry sftpFileAttr::entry() {
- UDSEntry entry;
- UDSAtom atom;
-
- atom.m_uds = UDS_NAME;
- atom.m_str = mFilename;
- entry.append(atom);
-
- if( mFlags & SSH2_FILEXFER_ATTR_SIZE ) {
- atom.m_uds = UDS_SIZE;
- atom.m_long = mSize;
- entry.append(atom);
- }
-
- if( mFlags & SSH2_FILEXFER_ATTR_ACMODTIME ) {
- atom.m_uds = UDS_ACCESS_TIME;
- atom.m_long = mAtime;
- entry.append(atom);
-
- atom.m_uds = UDS_MODIFICATION_TIME;
- atom.m_long = mMtime;
- entry.append(atom);
- }
-
- if( mFlags & SSH2_FILEXFER_ATTR_UIDGID ) {
- if( mUserName.isEmpty() || mGroupName.isEmpty() )
- getUserGroupNames();
-
- atom.m_uds = UDS_USER;
- atom.m_str = mUserName;
- entry.append(atom);
-
- atom.m_uds = UDS_GROUP;
- atom.m_str = mGroupName;
- entry.append(atom);
- }
-
- if( mFlags & SSH2_FILEXFER_ATTR_PERMISSIONS ) {
- atom.m_uds = UDS_ACCESS;
- atom.m_long = mPermissions;
- entry.append(atom);
-
- mode_t type = fileType();
-
- // Set the type if we know what it is
- if( type != 0 ) {
- atom.m_uds = UDS_FILE_TYPE;
- atom.m_long = (mLinkType ? mLinkType:type);
- entry.append(atom);
- }
-
- if( S_ISLNK(type) ) {
- atom.m_uds = UDS_LINK_DEST;
- atom.m_str = mLinkDestination;
- entry.append(atom);
- }
- }
-
- return entry;
-}
-
-/** Use to output the file attributes to a sftp packet */
-TQDataStream& operator<< (TQDataStream& s, const sftpFileAttr& fa) {
- s << (TQ_UINT32)fa.mFlags;
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_SIZE )
- { s << (TQ_ULLONG)fa.mSize; }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_UIDGID )
- { s << (TQ_UINT32)fa.mUid << (TQ_UINT32)fa.mGid; }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_PERMISSIONS )
- { s << (TQ_UINT32)fa.mPermissions; }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_ACMODTIME )
- { s << (TQ_UINT32)fa.mAtime << (TQ_UINT32)fa.mMtime; }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_EXTENDED ) {
- s << (TQ_UINT32)fa.mExtendedCount;
- // XXX: Write extensions to data stream here
- // s.writeBytes(extendedtype).writeBytes(extendeddata);
- }
- return s;
-}
-
-
-/** Use to read a file attribute from a sftp packet */
-TQDataStream& operator>> (TQDataStream& s, sftpFileAttr& fa) {
-
- // XXX Add some error checking in here in case
- // we get a bad sftp packet.
-
- fa.clear();
-
- if( fa.mDirAttrs ) {
- TQCString fn;
- s >> fn;
- fn.truncate( fn.size() );
-
- fa.mFilename = fa.mEncoding->decode( fn );
-
- s >> fa.mLongname;
- fa.mLongname.truncate( fa.mLongname.size() );
- //kdDebug() << ">>: ftpfileattr long filename (" << fa.mLongname.size() << ")= " << fa.mLongname << endl;
- }
-
- s >> fa.mFlags; // get flags
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_SIZE ) {
- TQ_ULLONG fileSize;
- s >> fileSize;
- fa.setFileSize(fileSize);
- }
-
- TQ_UINT32 x;
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_UIDGID ) {
- s >> x; fa.setUid(x);
- s >> x; fa.setGid(x);
- }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_PERMISSIONS ) {
- s >> x; fa.setPermissions(x);
- }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_ACMODTIME ) {
- s >> x; fa.setAtime(x);
- s >> x; fa.setMtime(x);
- }
-
- if( fa.mFlags & SSH2_FILEXFER_ATTR_EXTENDED ) {
- s >> x; fa.setExtendedCount(x);
- // XXX: Read in extensions from data stream here
- // s.readBytes(extendedtype).readBytes(extendeddata);
- }
-
- fa.getUserGroupNames();
- return s;
-}
-/** Parse longname for the owner and group names. */
-void sftpFileAttr::getUserGroupNames(){
- // Get the name of the owner and group of the file from longname.
- TQString user, group;
- if( mLongname.isEmpty() ) {
- // do not have the user name so use the user id instead
- user.setNum(mUid);
- group.setNum(mGid);
- }
- else {
- int field = 0;
- int i = 0;
- int l = mLongname.length();
-
- TQString longName = mEncoding->decode( mLongname );
-
- kdDebug(7120) << "Decoded: " << longName << endl;
-
- // Find the beginning of the third field which contains the user name.
- while( field != 2 ) {
- if( longName[i].isSpace() ) {
- field++; i++;
- while( i < l && longName[i].isSpace() ) { i++; }
- }
- else { i++; }
- }
- // i is the index of the first character of the third field.
- while( i < l && !longName[i].isSpace() ) {
- user.append(longName[i]);
- i++;
- }
-
- // i is the first character of the space between fields 3 and 4
- // user contains the owner's user name
- while( i < l && longName[i].isSpace() ) {
- i++;
- }
-
- // i is the first character of the fourth field
- while( i < l && !longName[i].isSpace() ) {
- group.append(longName[i]);
- i++;
- }
- // group contains the name of the group.
- }
-
- mUserName = user;
- mGroupName = group;
-}
-
-/** No descriptions */
-kdbgstream& operator<< (kdbgstream& s, sftpFileAttr& a) {
- s << "Filename: " << a.mFilename
- << ", Uid: " << a.mUid
- << ", Gid: " << a.mGid
- << ", Username: " << a.mUserName
- << ", GroupName: " << a.mGroupName
- << ", Permissions: " << a.mPermissions
- << ", size: " << a.mSize
- << ", atime: " << a.mAtime
- << ", mtime: " << a.mMtime
- << ", extended cnt: " << a.mExtendedCount;
-
- if (S_ISLNK(a.mLinkType)) {
- s << ", Link Type: " << a.mLinkType;
- s << ", Link Destination: " << a.mLinkDestination;
- }
-
- return s;
-}
-
-/** Make sure it builds with NDEBUG */
-kndbgstream& operator<< (kndbgstream& s, sftpFileAttr& ) {
- return s;
-}
-
-/** Clear all attributes and flags. */
-void sftpFileAttr::clear(){
- clearAtime();
- clearMtime();
- clearGid();
- clearUid();
- clearFileSize();
- clearPermissions();
- clearExtensions();
- mFilename = TQString::null;
- mGroupName = TQString::null;
- mUserName = TQString::null;
- mLinkDestination = TQString::null;
- mFlags = 0;
- mLongname = "\0";
- mLinkType = 0;
-}
-
-/** Return the size of the sftp attribute. */
-TQ_UINT32 sftpFileAttr::size() const{
- TQ_UINT32 size = 4; // for the attr flag
- if( mFlags & SSH2_FILEXFER_ATTR_SIZE )
- size += 8;
-
- if( mFlags & SSH2_FILEXFER_ATTR_UIDGID )
- size += 8;
-
- if( mFlags & SSH2_FILEXFER_ATTR_PERMISSIONS )
- size += 4;
-
- if( mFlags & SSH2_FILEXFER_ATTR_ACMODTIME )
- size += 8;
-
- if( mFlags & SSH2_FILEXFER_ATTR_EXTENDED ) {
- size += 4;
- // add size of extensions
- }
- return size;
-}
-
-/** Returns the file type as determined from the file permissions */
-mode_t sftpFileAttr::fileType() const{
- mode_t type = 0;
-
- if( S_ISLNK(mPermissions) )
- type |= S_IFLNK;
-
- if( S_ISREG(mPermissions) )
- type |= S_IFREG;
- else if( S_ISDIR(mPermissions) )
- type |= S_IFDIR;
- else if( S_ISCHR(mPermissions) )
- type |= S_IFCHR;
- else if( S_ISBLK(mPermissions) )
- type |= S_IFBLK;
- else if( S_ISFIFO(mPermissions) )
- type |= S_IFIFO;
- else if( S_ISSOCK(mPermissions) )
- type |= S_IFSOCK;
-
- return type;
-}
-
-void sftpFileAttr::setEncoding( KRemoteEncoding* encoding )
-{
- mEncoding = encoding;
-}
diff --git a/tdeioslave/sftp/sftpfileattr.h b/tdeioslave/sftp/sftpfileattr.h
deleted file mode 100644
index 43b56979d..000000000
--- a/tdeioslave/sftp/sftpfileattr.h
+++ /dev/null
@@ -1,261 +0,0 @@
-/***************************************************************************
- sftpfileattr.h - description
- -------------------
- begin : Sat Jun 30 2001
- copyright : (C) 2001 by Lucas Fisher
- email : ljfisher@iastate.edu
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
-#ifndef SFTPFILEATTR_H
-#define SFTPFILEATTR_H
-
-#include <sys/types.h>
-
-#include <tqglobal.h>
-#include <tqstring.h>
-#include <tqdatastream.h>
-
-#include <tdeio/global.h>
-#include <kdebug.h>
-
-#include "sftp.h"
-
-/**
- *@author Lucas Fisher
- */
-
-class KRemoteEncoding;
-
-class sftpFileAttr {
-
-private: // Private attributes
- /** Name of file. */
- TQString mFilename;
-
- /** Specifies which fields of the file attribute are available. */
- TQ_UINT32 mFlags;
-
- /** Size of the file in bytes. Should be 64 bit safe. */
- TQ_ULLONG mSize;
-
- /** User id of the owner of the file. */
- uid_t mUid;
-
- /** Group id of the group to which the file belongs. */
- gid_t mGid;
-
- /** POSIX permissions of the file. */
- mode_t mPermissions;
-
- /** Last access time of the file in seconds from Jan 1, 1970. */
- time_t mAtime;
-
- /** Last modification time of file in seconds since Jan. 1, 1970. */
- time_t mMtime;
-
- /** Number of file attribute extensions.
- Not currently implemented */
- TQ_UINT32 mExtendedCount;
-
- /** Longname of the file as found in a SSH_FXP_NAME sftp packet.
- These contents are parse to return the file's owner name and
- gr oup name. */
- TQCString mLongname;
-
- TQString mUserName;
- TQString mGroupName;
-
- /** If file is a link, contains the destination of the link */
- TQString mLinkDestination;
-
- /** If resource is a link, contains the type the link,e.g. file,dir... */
- mode_t mLinkType;
-
- /** Whether >> operator should read filename and longname from the stream. */
- bool mDirAttrs;
-
- /** Holds the encoding of the remote host */
- KRemoteEncoding* mEncoding;
-
-public:
- sftpFileAttr();
-
- sftpFileAttr(KRemoteEncoding* encoding);
-
- ~sftpFileAttr();
-
- /** Constructor to initialize the file attributes on declaration. */
- sftpFileAttr(TQ_ULLONG size_, uid_t uid_, gid_t gid_, mode_t permissions_,
- time_t atime_, time_t mtime_, TQ_UINT32 extendedCount_ = 0);
-
- /** Return the size of the sftp attribute not including filename or longname*/
- TQ_UINT32 size() const;
-
- /** Clear all attributes and flags. */
- void clear();
-
- /** Set the size of the file. */
- void setFileSize(TQ_ULLONG s)
- { mSize = s; mFlags |= SSH2_FILEXFER_ATTR_SIZE; }
-
- /** The size file attribute will not be included in the UDSEntry
- or when the file attribute is written to the sftp packet. */
- void clearFileSize()
- { mSize = 0; mFlags &= ~SSH2_FILEXFER_ATTR_SIZE; }
-
- /** Returns the size of the file. */
- TQ_ULLONG fileSize() const { return mSize; }
-
- /** Sets the POSIX permissions of the file. */
- void setPermissions(mode_t p)
- { mPermissions = p; mFlags |= SSH2_FILEXFER_ATTR_PERMISSIONS; }
-
- /** The permissions file attribute will not be included in the UDSEntry
- or when the file attribute is written to the sftp packet. */
- void clearPermissions()
- { mPermissions = 0; mFlags &= ~SSH2_FILEXFER_ATTR_PERMISSIONS; }
-
- /** Returns the POSIX permissons of the file. */
- mode_t permissions() const { return mPermissions; }
-
- /** Sets the group id of the file. */
- void setGid(gid_t id)
- { mGid = id; mFlags |= SSH2_FILEXFER_ATTR_UIDGID; }
-
- /** Neither the gid or uid file attributes will not be included in the UDSEntry
- or when the file attribute is written to the sftp packet. This is
- equivalent to clearUid() */
- void clearGid()
- { mGid = 0; mFlags &= SSH2_FILEXFER_ATTR_UIDGID; }
-
- /** Returns the group id of the file. */
- gid_t gid() const { return mGid; }
-
- /** Sets the uid of the file. */
- void setUid(uid_t id)
- { mUid = id; mFlags |= SSH2_FILEXFER_ATTR_UIDGID; }
-
- /** Neither the gid or uid file attributes will not be included in the UDSEntry
- or when the file attribute is written to the sftp packet. This is
- equivalent to clearGid() */
- void clearUid()
- { mUid = 0; mFlags &= SSH2_FILEXFER_ATTR_UIDGID; }
-
- /** Returns the user id of the file. */
- gid_t uid() const { return mUid; }
-
- /** Set the modificatoin time of the file in seconds since Jan. 1, 1970. */
- void setMtime(time_t t)
- { mMtime = t; mFlags |= SSH2_FILEXFER_ATTR_ACMODTIME; }
-
- /** Neither the mtime or atime file attributes will not be included in the UDSEntry
- or when the file attribute is written to the sftp packet. This is
- equivalent to clearAtime() */
- void clearMtime()
- { mMtime = 0; mFlags &= SSH2_FILEXFER_ATTR_ACMODTIME; }
-
- /** Returns the modification time of the file in seconds since Jan. 1, 1970. */
- time_t mtime() const { return mMtime; }
-
- /** Sets the access time of the file in seconds since Jan. 1, 1970. */
- void setAtime(time_t t)
- { mAtime = t; mFlags |= SSH2_FILEXFER_ATTR_ACMODTIME; }
-
- /** Neither the atime or mtime file attributes will not be included in the UDSEntry
- or when the file attribute is written to the sftp packet. This is
- equivalent to clearMtime() */
- void clearAtime()
- { mAtime = 0; mFlags &= SSH2_FILEXFER_ATTR_ACMODTIME; }
-
- /** Returns the last access time of the file in seconds since Jan. 1, 1970. */
- time_t atime() const { return mAtime; }
-
- /** Sets the number of file attribute extensions. */
- void setExtendedCount(unsigned int c)
- { mExtendedCount = c; mFlags |= SSH2_FILEXFER_ATTR_EXTENDED; }
-
- /** No extensions will be included when the file attribute is written
- to a sftp packet. */
- void clearExtensions()
- { mExtendedCount = 0; mFlags &= ~SSH2_FILEXFER_ATTR_EXTENDED; }
-
- /** Returns the number of file attribute extentsions. */
- unsigned int extendedCount() const { return mExtendedCount; }
-
- /** Returns the flags for the sftp file attributes. */
- unsigned int flags() const { return mFlags; }
-
- /** Sets file's longname. See sftpFileAttr::longname. */
- void setLongname(TQString l) { mLongname = l.latin1(); }
-
- /** Returns a string describing the file attributes. The format is specific
- to the implementation of the sftp server. In most cases (ie OpenSSH)
- this is similar to the long output of 'ls'. */
- TQString longname() const { return mLongname; }
-
- void setLinkDestination(const TQString& target)
- { mLinkDestination = target; }
-
- TQString linkDestination()
- { return mLinkDestination; }
-
- /** Sets the actual type a symbolic link points to. */
- void setLinkType (mode_t type) { mLinkType = type; }
-
- mode_t linkType() const { return mLinkType; }
-
- /** No descriptions */
- void setFilename(const TQString& fn)
- { mFilename = fn; }
-
- TQString filename() const
- { return mFilename; }
-
- /** Returns a UDSEntry describing the file.
- The UDSEntry is generated from the sftp file attributes. */
- TDEIO::UDSEntry entry();
-
- /** Use to output the file attributes to a sftp packet
- This will only write the sftp ATTR structure to the stream.
- It will never write the filename and longname because the client
- never sends those to the server. */
- friend TQDataStream& operator<< (TQDataStream&, const sftpFileAttr&);
-
- /** Use to read a file attribute from a sftp packet.
- Read this carefully! If the DirAttrs flag is true, this will
- read the filename, longname, and file attributes from the stream.
- This is for use with listing directories.
- If the DirAttrs flag is false, this will only read file attributes
- from the stream.
- BY DEFAULT, A NEW INSTANCE HAS DirAttrs == false */
- friend TQDataStream& operator>> (TQDataStream&, sftpFileAttr&);
-
- /** Parse longname for the owner and group names. */
- void getUserGroupNames();
-
- /** Sets the DirAttrs flag. This flag affects how the >> operator works on data streams. */
- void setDirAttrsFlag(bool flag){ mDirAttrs = flag; }
-
- /** Gets the DirAttrs flag. */
- bool getDirAttrsFlag() const { return mDirAttrs; }
-
- friend kdbgstream& operator<< (kdbgstream& s, sftpFileAttr& a);
- friend kndbgstream& operator<< (kndbgstream& s, sftpFileAttr& a);
-
- /** Returns the file type as determined from the file permissions */
- mode_t fileType() const;
-
- /** Set the encoding of the remote file system */
- void setEncoding( KRemoteEncoding* encoding );
-};
-
-#endif
diff --git a/tdeioslave/sftp/tdeio_sftp.cpp b/tdeioslave/sftp/tdeio_sftp.cpp
index eab0eae42..9203f8537 100644
--- a/tdeioslave/sftp/tdeio_sftp.cpp
+++ b/tdeioslave/sftp/tdeio_sftp.cpp
@@ -1,62 +1,52 @@
-/***************************************************************************
- sftp.cpp - description
- -------------------
- begin : Fri Jun 29 23:45:40 CDT 2001
- copyright : (C) 2001 by Lucas Fisher
- email : ljfisher@purdue.edu
- ***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
-
/*
-DEBUGGING
-We are pretty much left with kdDebug messages for debugging. We can't use a gdb
-as described in the ioslave DEBUG.howto because tdeinit has to run in a terminal.
-Ssh will detect this terminal and ask for a password there, but will just get garbage.
-So we can't connect.
-*/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+ * Copyright (c) 2001 Lucas Fisher <ljfisher@purdue.edu>
+ * Copyright (c) 2009 Andreas Schneider <mail@cynapses.org>
+ * Copyright (c) 2020 Martin Sandsmark <martin@sandsmark.ninja>
+ * KDE2 port
+ * Copyright (c) 2022 Mavridis Philippe <mavridisf@gmail.com>
+ * Trinity port
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "tdeio_sftp.h"
#include <fcntl.h>
-#include <tqcstring.h>
-#include <tqstring.h>
-#include <tqobject.h>
-#include <tqstrlist.h>
+#include <tqapplication.h>
#include <tqfile.h>
-#include <tqbuffer.h>
+#include <tqdir.h>
#include <stdlib.h>
#include <unistd.h>
-#include <signal.h>
#include <errno.h>
-#include <ctype.h>
#include <time.h>
-#include <netdb.h>
#include <string.h>
-#include <netinet/in.h>
#include <arpa/inet.h>
+#include <netinet/in.h>
+
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <tdeapplication.h>
-#include <kuser.h>
#include <kdebug.h>
#include <tdemessagebox.h>
-#include <kinstance.h>
#include <tdeglobal.h>
#include <kstandarddirs.h>
#include <tdelocale.h>
@@ -64,22 +54,21 @@ So we can't connect.
#include <tdeio/ioslave_defaults.h>
#include <kmimetype.h>
#include <kmimemagic.h>
-#include <klargefile.h>
-#include <kremoteencoding.h>
+#include <signal.h>
-#include "sftp.h"
-#include "tdeio_sftp.h"
-#include "atomicio.h"
-#include "sftpfileattr.h"
-#include "ksshprocess.h"
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+#include <libssh/callbacks.h>
+#define TDEIO_SFTP_SPECIAL_TIMEOUT 30
+#define ZERO_STRUCTP(x) do { if ((x) != NULL) memset((char *)(x), 0, sizeof(*(x))); } while(0)
using namespace TDEIO;
extern "C"
{
- int KDE_EXPORT kdemain( int argc, char **argv )
+ int kdemain( int argc, char **argv )
{
- TDEInstance instance( "tdeio_sftp" );
+ TDEInstance instance( "tdeio_sftp" );
kdDebug(TDEIO_SFTP_DB) << "*** Starting tdeio_sftp " << endl;
@@ -87,8 +76,13 @@ extern "C"
kdDebug(TDEIO_SFTP_DB) << "Usage: tdeio_sftp protocol domain-socket1 domain-socket2" << endl;
exit(-1);
}
-
sftpProtocol slave(argv[2], argv[3]);
+
+ if (getenv("DEBUG_TDEIO_SFTP")) {
+ // Give us a coredump in the journal
+ signal(6, SIG_DFL);
+ }
+
slave.dispatchLoop();
kdDebug(TDEIO_SFTP_DB) << "*** tdeio_sftp Done" << endl;
@@ -96,2187 +90,1684 @@ extern "C"
}
}
-
-/*
- * This helper handles some special issues (blocking and interrupted
- * system call) when writing to a file handle.
- *
- * @return 0 on success or an error code on failure (ERR_COULD_NOT_WRITE,
- * ERR_DISK_FULL, ERR_CONNECTION_BROKEN).
- */
-static int writeToFile (int fd, const char *buf, size_t len)
-{
- while (len > 0)
- {
- ssize_t written = ::write(fd, buf, len);
- if (written >= 0)
- {
- buf += written;
- len -= written;
- continue;
- }
-
- switch(errno)
- {
- case EINTR:
- continue;
- case EPIPE:
- return ERR_CONNECTION_BROKEN;
- case ENOSPC:
- return ERR_DISK_FULL;
- default:
- return ERR_COULD_NOT_WRITE;
- }
+// The callback function for libssh
+int auth_callback(const char *prompt, char *buf, size_t len,
+ int echo, int verify, void *userdata) {
+ if (userdata == NULL) {
+ return -1;
}
- return 0;
-}
-sftpProtocol::sftpProtocol(const TQCString &pool_socket, const TQCString &app_socket)
- : SlaveBase("tdeio_sftp", pool_socket, app_socket),
- mConnected(false), mPort(-1), mMsgId(0) {
- kdDebug(TDEIO_SFTP_DB) << "sftpProtocol(): pid = " << getpid() << endl;
-}
+ sftpProtocol *slave = (sftpProtocol *) userdata;
-
-sftpProtocol::~sftpProtocol() {
- kdDebug(TDEIO_SFTP_DB) << "~sftpProtocol(): pid = " << getpid() << endl;
- closeConnection();
-}
-
-/**
- * Type is a sftp packet type found in .sftp.h'.
- * Example: SSH2_FXP_READLINK, SSH2_FXP_RENAME, etc.
- *
- * Returns true if the type is supported by the sftp protocol
- * version negotiated by the client and server (sftpVersion).
- */
-bool sftpProtocol::isSupportedOperation(int type) {
- switch (type) {
- case SSH2_FXP_VERSION:
- case SSH2_FXP_STATUS:
- case SSH2_FXP_HANDLE:
- case SSH2_FXP_DATA:
- case SSH2_FXP_NAME:
- case SSH2_FXP_ATTRS:
- case SSH2_FXP_INIT:
- case SSH2_FXP_OPEN:
- case SSH2_FXP_CLOSE:
- case SSH2_FXP_READ:
- case SSH2_FXP_WRITE:
- case SSH2_FXP_LSTAT:
- case SSH2_FXP_FSTAT:
- case SSH2_FXP_SETSTAT:
- case SSH2_FXP_FSETSTAT:
- case SSH2_FXP_OPENDIR:
- case SSH2_FXP_READDIR:
- case SSH2_FXP_REMOVE:
- case SSH2_FXP_MKDIR:
- case SSH2_FXP_RMDIR:
- case SSH2_FXP_REALPATH:
- case SSH2_FXP_STAT:
- return true;
- case SSH2_FXP_RENAME:
- return sftpVersion >= 2 ? true : false;
- case SSH2_FXP_EXTENDED:
- case SSH2_FXP_EXTENDED_REPLY:
- case SSH2_FXP_READLINK:
- case SSH2_FXP_SYMLINK:
- return sftpVersion >= 3 ? true : false;
- default:
- kdDebug(TDEIO_SFTP_DB) << "isSupportedOperation(type:"
- << type << "): unrecognized operation type" << endl;
- break;
+ if (slave->auth_callback(prompt, buf, len, echo, verify, userdata) < 0) {
+ return -1;
}
- return false;
+ return 0;
}
-void sftpProtocol::copy(const KURL &src, const KURL &dest, int permissions, bool overwrite)
-{
- kdDebug(TDEIO_SFTP_DB) << "copy(): " << src << " -> " << dest << endl;
+void log_callback(ssh_session session, int priority, const char *message,
+ void *userdata) {
+ if (userdata == NULL) {
+ return;
+ }
- bool srcLocal = src.isLocalFile();
- bool destLocal = dest.isLocalFile();
+ sftpProtocol *slave = (sftpProtocol *) userdata;
- if ( srcLocal && !destLocal ) // Copy file -> sftp
- sftpCopyPut(src, dest, permissions, overwrite);
- else if ( destLocal && !srcLocal ) // Copy sftp -> file
- sftpCopyGet(dest, src, permissions, overwrite);
- else
- error(ERR_UNSUPPORTED_ACTION, TQString::null);
+ slave->log_callback(session, priority, message, userdata);
}
-void sftpProtocol::sftpCopyGet(const KURL& dest, const KURL& src, int mode, bool overwrite)
-{
- kdDebug(TDEIO_SFTP_DB) << "sftpCopyGet(): " << src << " -> " << dest << endl;
+int sftpProtocol::auth_callback(const char *prompt, char *buf, size_t len,
+ int echo, int verify, void *userdata) {
+ TQString i_prompt = TQString::fromUtf8(prompt);
- // Attempt to establish a connection...
- openConnection();
- if( !mConnected )
- return;
+ // unused variables
+ (void) echo;
+ (void) verify;
+ (void) userdata;
- KDE_struct_stat buff_orig;
- TQCString dest_orig ( TQFile::encodeName(dest.path()) );
- bool origExists = (KDE_lstat( dest_orig.data(), &buff_orig ) != -1);
+ kdDebug(TDEIO_SFTP_DB) << "Entering authentication callback, prompt=" << i_prompt << endl;
- if (origExists)
- {
- if (S_ISDIR(buff_orig.st_mode))
- {
- error(ERR_IS_DIRECTORY, dest.prettyURL());
- return;
- }
+ TDEIO::AuthInfo info;
- if (!overwrite)
- {
- error(ERR_FILE_ALREADY_EXIST, dest.prettyURL());
- return;
- }
- }
-
- TDEIO::filesize_t offset = 0;
- TQCString dest_part ( dest_orig + ".part" );
+ info.url.setProtocol("sftp");
+ info.url.setHost(mHost);
+ info.url.setPort(mPort);
+ info.url.setUser(mUsername);
- int fd = -1;
- bool partExists = false;
- bool markPartial = config()->readBoolEntry("MarkPartial", true);
+ info.comment = "sftp://" + mUsername + "@" + mHost;
+ info.username = i_prompt;
+ info.readOnly = true;
+ info.prompt = i_prompt;
+ info.keepPassword = false; // don't save passwords for public key,
+ // that's the task of ssh-agent.
- if (markPartial)
- {
- KDE_struct_stat buff_part;
- partExists = (KDE_stat( dest_part.data(), &buff_part ) != -1);
-
- if (partExists && buff_part.st_size > 0 && S_ISREG(buff_part.st_mode))
- {
- if (canResume( buff_part.st_size ))
- {
- offset = buff_part.st_size;
- kdDebug(TDEIO_SFTP_DB) << "sftpCopyGet: Resuming @ " << offset << endl;
- }
- }
+ if (!openPassDlg(info)) {
+ kdDebug(TDEIO_SFTP_DB) << "Password dialog failed" << endl;
+ return -1;
+ }
- if (offset > 0)
- {
- fd = KDE_open(dest_part.data(), O_RDWR);
- offset = KDE_lseek(fd, 0, SEEK_END);
- if (offset == 0)
- {
- error(ERR_CANNOT_RESUME, dest.prettyURL());
- return;
- }
- }
- else
- {
- // Set up permissions properly, based on what is done in file io-slave
- int openFlags = (O_CREAT | O_TRUNC | O_WRONLY);
- int initialMode = (mode == -1) ? 0666 : (mode | S_IWUSR);
- fd = KDE_open(dest_part.data(), openFlags, initialMode);
- }
- }
- else
- {
- // Set up permissions properly, based on what is done in file io-slave
- int openFlags = (O_CREAT | O_TRUNC | O_WRONLY);
- int initialMode = (mode == -1) ? 0666 : (mode | S_IWUSR);
- fd = KDE_open(dest_orig.data(), openFlags, initialMode);
- }
+ strncpy(buf, info.password.utf8().data(), len - 1);
- if(fd == -1)
- {
- kdDebug(TDEIO_SFTP_DB) << "sftpCopyGet: Unable to open (" << fd << ") for writting." << endl;
- if (errno == EACCES)
- error (ERR_WRITE_ACCESS_DENIED, dest.prettyURL());
- else
- error (ERR_CANNOT_OPEN_FOR_WRITING, dest.prettyURL());
- return;
- }
+ info.password.fill('x');
- Status info = sftpGet(src, offset, fd);
- if ( info.code != 0 )
- {
- // Should we keep the partially downloaded file ??
- TDEIO::filesize_t size = config()->readNumEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
- if (info.size < size)
- ::remove(dest_part.data());
+ return 0;
+}
- error(info.code, info.text);
- return;
- }
+void sftpProtocol::log_callback(ssh_session session, int priority,
+ const char *message, void *userdata) {
+ (void) session;
+ (void) userdata;
- if (::close(fd) != 0)
- {
- error(ERR_COULD_NOT_WRITE, dest.prettyURL());
- return;
- }
+ kdDebug(TDEIO_SFTP_DB) << "[" << priority << "] " << message << endl;
+}
- //
- if (markPartial)
- {
- if (::rename(dest_part.data(), dest_orig.data()) != 0)
- {
- error (ERR_CANNOT_RENAME_PARTIAL, dest_part);
- return;
- }
- }
+int sftpProtocol::authenticateKeyboardInteractive(AuthInfo &info) {
+ TQString name, instruction, prompt;
+ int err = SSH_AUTH_ERROR;
- data(TQByteArray());
- kdDebug(TDEIO_SFTP_DB) << "sftpCopyGet(): emit finished()" << endl;
- finished();
-}
+ kdDebug(TDEIO_SFTP_DB) << "Entering keyboard interactive function" << endl;
-sftpProtocol::Status sftpProtocol::sftpGet( const KURL& src, TDEIO::filesize_t offset, int fd )
-{
- int code;
- sftpFileAttr attr(remoteEncoding());
+ err = ssh_userauth_kbdint(mSession, mUsername.utf8().data(), NULL);
+ while (err == SSH_AUTH_INFO) {
+ int n = 0;
+ int i = 0;
- Status res;
- res.code = 0;
- res.size = 0;
+ name = TQString::fromUtf8(ssh_userauth_kbdint_getname(mSession));
+ instruction = TQString::fromUtf8(ssh_userauth_kbdint_getinstruction(mSession));
+ n = ssh_userauth_kbdint_getnprompts(mSession);
- kdDebug(TDEIO_SFTP_DB) << "sftpGet(): " << src << endl;
+ kdDebug(TDEIO_SFTP_DB) << "name=" << name << " instruction=" << instruction
+ << " prompts" << n << endl;
- // stat the file first to get its size
- if( (code = sftpStat(src, attr)) != SSH2_FX_OK ) {
- return doProcessStatus(code, src.prettyURL());
- }
+ for (i = 0; i < n; ++i) {
+ char echo;
+ const char *answer = "";
- // We cannot get file if it is a directory
- if( attr.fileType() == S_IFDIR ) {
- res.text = src.prettyURL();
- res.code = ERR_IS_DIRECTORY;
- return res;
- }
+ prompt = TQString::fromUtf8(ssh_userauth_kbdint_getprompt(mSession, i, &echo));
+ kdDebug(TDEIO_SFTP_DB) << "prompt=" << prompt << " echo=" << TQString::number(echo) << endl;
+ if (echo) {
+ // See RFC4256 Section 3.3 User Interface
+ TQString newPrompt;
+ TDEIO::AuthInfo infoKbdInt;
- TDEIO::filesize_t fileSize = attr.fileSize();
- TQ_UINT32 pflags = SSH2_FXF_READ;
- attr.clear();
+ infoKbdInt.url.setProtocol("sftp");
+ infoKbdInt.url.setHost(mHost);
+ infoKbdInt.url.setPort(mPort);
- TQByteArray handle;
- if( (code = sftpOpen(src, pflags, attr, handle)) != SSH2_FX_OK ) {
- res.text = src.prettyURL();
- res.code = ERR_CANNOT_OPEN_FOR_READING;
- return res;
- }
+ infoKbdInt.caption = i18n("SFTP Login");
+ infoKbdInt.comment = "sftp://" + mUsername + "@" + mHost;
- // needed for determining mimetype
- // note: have to emit mimetype before emitting totalsize.
- TQByteArray buff;
- TQByteArray mimeBuffer;
-
- unsigned int oldSize;
- bool foundMimetype = false;
-
- // How big should each data packet be? Definitely not bigger than 64kb or
- // you will overflow the 2 byte size variable in a sftp packet.
- TQ_UINT32 len = 60*1024;
- code = SSH2_FX_OK;
-
- kdDebug(TDEIO_SFTP_DB) << "sftpGet(): offset = " << offset << endl;
- while( code == SSH2_FX_OK ) {
- if( (code = sftpRead(handle, offset, len, buff)) == SSH2_FX_OK ) {
- offset += buff.size();
-
- // save data for mimetype. Pretty much follows what is in the ftp ioslave
- if( !foundMimetype ) {
- oldSize = mimeBuffer.size();
- mimeBuffer.resize(oldSize + buff.size());
- memcpy(mimeBuffer.data()+oldSize, buff.data(), buff.size());
-
- if( mimeBuffer.size() > 1024 || offset == fileSize ) {
- // determine mimetype
- KMimeMagicResult* result =
- KMimeMagic::self()->findBufferFileType(mimeBuffer, src.fileName());
- kdDebug(TDEIO_SFTP_DB) << "sftpGet(): mimetype is " <<
- result->mimeType() << endl;
- mimeType(result->mimeType());
-
- // Always send the total size after emitting mime-type...
- totalSize(fileSize);
-
- if (fd == -1)
- data(mimeBuffer);
- else
- {
- if ( (res.code=writeToFile(fd, mimeBuffer.data(), mimeBuffer.size())) != 0 )
- return res;
- }
-
- processedSize(mimeBuffer.size());
- mimeBuffer.resize(0);
- foundMimetype = true;
- }
- }
- else {
- if (fd == -1)
- data(buff);
- else
- {
- if ( (res.code= writeToFile(fd, buff.data(), buff.size())) != 0 )
- return res;
- }
- processedSize(offset);
- }
+ if (!name.isEmpty()) {
+ infoKbdInt.caption = TQString(i18n("SFTP Login") + " - " + name);
}
- /*
- Check if slave was killed. According to slavebase.h we need to leave
- the slave methods as soon as possible if the slave is killed. This
- allows the slave to be cleaned up properly.
- */
- if( wasKilled() ) {
- res.text = i18n("An internal error occurred. Please retry the request again.");
- res.code = ERR_UNKNOWN;
- return res;
+ if (!instruction.isEmpty()) {
+ newPrompt = instruction + "\n\n";
}
- }
- if( code != SSH2_FX_EOF ) {
- res.text = src.prettyURL();
- res.code = ERR_COULD_NOT_READ; // return here or still send empty array to indicate end of read?
- }
+ newPrompt.append(prompt + "\n\n");
+ infoKbdInt.readOnly = false;
+ infoKbdInt.keepPassword = false;
+ infoKbdInt.prompt = i18n("Use the username input field to answer this question.");
- res.size = offset;
- sftpClose(handle);
- processedSize (offset);
- return res;
-}
-
-void sftpProtocol::get(const KURL& url) {
- kdDebug(TDEIO_SFTP_DB) << "get(): " << url << endl ;
- openConnection();
- if( !mConnected )
- return;
-
- // Get resume offset
- TQ_UINT64 offset = config()->readUnsignedLongNumEntry("resume");
- if( offset > 0 ) {
- canResume();
- kdDebug(TDEIO_SFTP_DB) << "get(): canResume(), offset = " << offset << endl;
- }
+ if (openPassDlg(infoKbdInt)) {
+ kdDebug(TDEIO_SFTP_DB) << "Got the answer from the password dialog" << endl;
+ answer = info.username.utf8().data();
+ }
- Status info = sftpGet(url, offset);
+ if (ssh_userauth_kbdint_setanswer(mSession, i, answer) < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "An error occurred setting the answer: "
+ << ssh_get_error(mSession) << endl;
+ return SSH_AUTH_ERROR;
+ }
+ break;
+ } else {
+ if (prompt.lower() == "password") {
+ answer = mPassword.utf8().data();
+ } else {
+ info.readOnly = true; // set username readonly
+ info.prompt = prompt;
+
+ if (openPassDlg(info)) {
+ kdDebug(TDEIO_SFTP_DB) << "Got the answer from the password dialog" << endl;
+ answer = info.password.utf8().data();
+ }
+ }
- if (info.code != 0)
- {
- error(info.code, info.text);
- return;
+ if (ssh_userauth_kbdint_setanswer(mSession, i, answer) < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "An error occurred setting the answer: "
+ << ssh_get_error(mSession) << endl;
+ return SSH_AUTH_ERROR;
+ }
+ }
}
+ err = ssh_userauth_kbdint(mSession, mUsername.utf8().data(), NULL);
+ }
- data(TQByteArray());
- kdDebug(TDEIO_SFTP_DB) << "get(): emit finished()" << endl;
- finished();
+ return err;
}
+void sftpProtocol::reportError(const KURL &url, const int err) {
+ kdDebug(TDEIO_SFTP_DB) << "url = " << url.url() << " - err=" << err << endl;
-void sftpProtocol::setHost (const TQString& h, int port, const TQString& user, const TQString& pass)
-{
- kdDebug(TDEIO_SFTP_DB) << "setHost(): " << user << "@" << h << ":" << port << endl;
-
- if( mHost != h || mPort != port || user != mUsername || mPassword != pass )
- closeConnection();
-
- mHost = h;
-
- if( port > 0 )
- mPort = port;
- else {
- mPort = -1;
- }
-
- mUsername = user;
- mPassword = pass;
-
- if (user.isEmpty())
- {
- KUser u;
- mUsername = u.loginName();
- }
+ switch (err) {
+ case SSH_FX_OK:
+ break;
+ case SSH_FX_NO_SUCH_FILE:
+ case SSH_FX_NO_SUCH_PATH:
+ error(TDEIO::ERR_DOES_NOT_EXIST, url.prettyURL());
+ break;
+ case SSH_FX_PERMISSION_DENIED:
+ error(TDEIO::ERR_ACCESS_DENIED, url.prettyURL());
+ break;
+ case SSH_FX_FILE_ALREADY_EXISTS:
+ error(TDEIO::ERR_FILE_ALREADY_EXIST, url.prettyURL());
+ break;
+ case SSH_FX_INVALID_HANDLE:
+ error(TDEIO::ERR_MALFORMED_URL, url.prettyURL());
+ break;
+ case SSH_FX_OP_UNSUPPORTED:
+ error(TDEIO::ERR_UNSUPPORTED_ACTION, url.prettyURL());
+ break;
+ case SSH_FX_BAD_MESSAGE:
+ error(TDEIO::ERR_UNKNOWN, url.prettyURL());
+ break;
+ default:
+ error(TDEIO::ERR_INTERNAL, url.prettyURL());
+ break;
+ }
}
+bool sftpProtocol::createUDSEntry(const TQString &filename, const TQByteArray &path,
+ UDSEntry &entry, short int details) {
+ mode_t type;
+ mode_t access;
+ char *link;
-void sftpProtocol::openConnection() {
-
- if(mConnected)
- return;
+ ASSERT(entry.count() == 0);
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): " << mUsername << "@"
- << mHost << ":" << mPort << endl;
-
- infoMessage( i18n("Opening SFTP connection to host <b>%1:%2</b>").arg(mHost).arg(mPort));
+ sftp_attributes sb = sftp_lstat(mSftp, path.data());
+ if (sb == NULL) {
+ return false;
+ }
- if( mHost.isEmpty() ) {
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): Need hostname..." << endl;
- error(ERR_UNKNOWN_HOST, i18n("No hostname specified"));
- return;
+ UDSAtom atom;
+ atom.m_uds = UDS_NAME;
+ atom.m_str = filename;
+ entry.append(atom);
+
+ if (sb->type == SSH_FILEXFER_TYPE_SYMLINK) {
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+ link = sftp_readlink(mSftp, path.data());
+ if (link == NULL) {
+ sftp_attributes_free(sb);
+ return false;
+ }
+ atom.m_uds = UDS_LINK_DEST;
+ atom.m_str = TQFile::decodeName(link);
+ entry.append(atom);
+ delete link;
+ // A symlink -> follow it only if details > 1
+ if (details > 1) {
+ sftp_attributes sb2 = sftp_stat(mSftp, path.data());
+ if (sb2 == NULL) {
+ // It is a link pointing to nowhere
+ type = S_IFMT - 1;
+ access = S_IRWXU | S_IRWXG | S_IRWXO;
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = type;
+ entry.append(atom);
+
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = access;
+ entry.append(atom);
+
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = 0LL;
+ entry.append(atom);
+
+ goto notype;
+ }
+ sftp_attributes_free(sb);
+ sb = sb2;
}
+ }
- ////////////////////////////////////////////////////////////////////////////
- // Setup AuthInfo for use with password caching and the
- // password dialog box.
- AuthInfo info;
- info.url.setProtocol("sftp");
- info.url.setHost(mHost);
- info.url.setPort(mPort);
- info.url.setUser(mUsername);
- info.caption = i18n("SFTP Login");
- info.comment = "sftp://" + mHost + ":" + TQString::number(mPort);
- info.commentLabel = i18n("site:");
- info.username = mUsername;
- info.keepPassword = true;
-
- ///////////////////////////////////////////////////////////////////////////
- // Check for cached authentication info if a username AND password were
- // not specified in setHost().
- if( mUsername.isEmpty() && mPassword.isEmpty() ) {
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): checking cache "
- << "info.username = " << info.username
- << ", info.url = " << info.url.prettyURL() << endl;
-
- if( checkCachedAuthentication(info) ) {
- mUsername = info.username;
- mPassword = info.password;
- }
- }
+ switch (sb->type) {
+ case SSH_FILEXFER_TYPE_REGULAR:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+ break;
+ case SSH_FILEXFER_TYPE_DIRECTORY:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR;
+ entry.append(atom);
+ break;
+ case SSH_FILEXFER_TYPE_SYMLINK:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFLNK;
+ entry.append(atom);
+ break;
+ case SSH_FILEXFER_TYPE_SPECIAL:
+ case SSH_FILEXFER_TYPE_UNKNOWN:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFMT - 1;
+ entry.append(atom);
+ break;
+ }
- ///////////////////////////////////////////////////////////////////////////
- // Now setup our ssh options. If we found a cached username
- // and password we set the SSH_PASSWORD and SSH_USERNAME
- // options right away. Otherwise we wait. The other options are
- // necessary for running sftp over ssh.
- KSshProcess::SshOpt opt; // a ssh option, this can be reused
- KSshProcess::SshOptList opts; // list of SshOpts
- KSshProcess::SshOptListIterator passwdIt; // points to the opt in opts that specifies the password
- KSshProcess::SshOptListIterator usernameIt;
-
-// opt.opt = KSshProcess::SSH_VERBOSE;
-// opts.append(opt);
-// opts.append(opt);
-
- if( mPort != -1 ) {
- opt.opt = KSshProcess::SSH_PORT;
- opt.num = mPort;
- opts.append(opt);
- }
+ access = sb->permissions & 07777;
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = access;
+ entry.append(atom);
+
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = sb->size;
+ entry.append(atom);
+
+notype:
+ if (details > 0) {
+ if (sb->owner) {
+ atom.m_uds = UDS_USER;
+ atom.m_str = TQString::fromUtf8(sb->owner);
+ entry.append(atom);
+ } else {
+ atom.m_uds = UDS_USER;
+ atom.m_str = TQString::number(sb->uid);
+ entry.append(atom);
+ }
+
+ if (sb->group) {
+ atom.m_uds = UDS_GROUP;
+ atom.m_str = TQString::fromUtf8(sb->group);
+ entry.append(atom);
+ } else {
+ atom.m_uds = UDS_GROUP;
+ atom.m_str = TQString::number(sb->gid);
+ entry.append(atom);
+ }
+ atom.m_uds = UDS_ACCESS_TIME;
+ atom.m_long = sb->atime;
+ entry.append(atom);
+
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ atom.m_long = sb->mtime;
+ entry.append(atom);
+
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ atom.m_long = sb->createtime;
+ entry.append(atom);
+ }
- opt.opt = KSshProcess::SSH_SUBSYSTEM;
- opt.str = "sftp";
- opts.append(opt);
+ sftp_attributes_free(sb);
- opt.opt = KSshProcess::SSH_FORWARDX11;
- opt.boolean = false;
- opts.append(opt);
+ return true;
+}
- opt.opt = KSshProcess::SSH_FORWARDAGENT;
- opt.boolean = false;
- opts.append(opt);
+TQString sftpProtocol::canonicalizePath(const TQString &path) {
+ kdDebug(TDEIO_SFTP_DB) << "Path to canonicalize: " << path << endl;
+ TQString cPath;
+ char *sPath = NULL;
- opt.opt = KSshProcess::SSH_PROTOCOL;
- opt.num = 2;
- opts.append(opt);
+ if (path.isEmpty()) {
+ return cPath;
+ }
- opt.opt = KSshProcess::SSH_HOST;
- opt.str = mHost;
- opts.append(opt);
+ sPath = sftp_canonicalize_path(mSftp, path.utf8().data());
+ if (sPath == NULL) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not canonicalize path: " << path << endl;
+ return cPath;
+ }
- opt.opt = KSshProcess::SSH_ESCAPE_CHAR;
- opt.num = -1; // don't use any escape character
- opts.append(opt);
+ cPath = TQFile::decodeName(sPath);
+ delete sPath;
- // set the username and password if we have them
- if( !mUsername.isEmpty() ) {
- opt.opt = KSshProcess::SSH_USERNAME;
- opt.str = mUsername;
- usernameIt = opts.append(opt);
- }
+ kdDebug(TDEIO_SFTP_DB) << "Canonicalized path: " << cPath << endl;
- if( !mPassword.isEmpty() ) {
- opt.opt = KSshProcess::SSH_PASSWD;
- opt.str = mPassword;
- passwdIt = opts.append(opt);
- }
+ return cPath;
+}
- ssh.setOptions(opts);
- ssh.printArgs();
-
- ///////////////////////////////////////////////////////////////////////////
- // Start the ssh connection process.
- //
-
- int err; // error code from KSshProcess
- TQString msg; // msg for dialog box
- TQString caption; // dialog box caption
- bool firstTime = true;
- bool dlgResult;
-
- while( !(mConnected = ssh.connect()) ) {
- err = ssh.error();
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): "
- "Got " << err << " from KSshProcess::connect()" << endl;
-
- switch(err) {
- case KSshProcess::ERR_NEED_PASSWD:
- case KSshProcess::ERR_NEED_PASSPHRASE:
- // At this point we know that either we didn't set
- // an username or password in the ssh options list,
- // or what we did pass did not work. Therefore we
- // must prompt the user.
- if( err == KSshProcess::ERR_NEED_PASSPHRASE )
- info.prompt = i18n("Please enter your username and key passphrase.");
- else
- info.prompt = i18n("Please enter your username and password.");
-
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): info.username = " << info.username
- << ", info.url = " << info.url.prettyURL() << endl;
-
- if( firstTime )
- dlgResult = openPassDlg(info);
- else
- dlgResult = openPassDlg(info, i18n("Incorrect username or password"));
-
- if( dlgResult ) {
- if( info.username.isEmpty() || info.password.isEmpty() ) {
- error(ERR_COULD_NOT_AUTHENTICATE,
- i18n("Please enter a username and password"));
- continue;
- }
- }
- else {
- // user canceled or dialog failed to open
- error(ERR_USER_CANCELED, TQString::null);
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): user canceled, dlgResult = " << dlgResult << endl;
- closeConnection();
- return;
- }
-
- firstTime = false;
-
- // Check if the username has changed. SSH only accepts
- // the username at startup. If the username has changed
- // we must disconnect ssh, change the SSH_USERNAME
- // option, and reset the option list. We will also set
- // the password option so the user is not prompted for
- // it again.
- if( mUsername != info.username ) {
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): Username changed from "
- << mUsername << " to " << info.username << endl;
-
- ssh.disconnect();
-
- // if we haven't yet added the username
- // or password option to the ssh options list then
- // the iterators will be equal to the empty iterator.
- // Create the opts now and add them to the opt list.
- if( usernameIt == KSshProcess::SshOptListIterator() ) {
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): "
- "Adding username to options list" << endl;
- opt.opt = KSshProcess::SSH_USERNAME;
- usernameIt = opts.append(opt);
- }
-
- if( passwdIt == KSshProcess::SshOptListIterator() ) {
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): "
- "Adding password to options list" << endl;
- opt.opt = KSshProcess::SSH_PASSWD;
- passwdIt = opts.append(opt);
- }
-
- (*usernameIt).str = info.username;
- (*passwdIt).str = info.password;
- ssh.setOptions(opts);
- ssh.printArgs();
- }
- else { // just set the password
- ssh.setPassword(info.password);
- }
-
- mUsername = info.username;
- mPassword = info.password;
-
- break;
-
- case KSshProcess::ERR_NEW_HOST_KEY:
- caption = i18n("Warning: Cannot verify host's identity.");
- msg = ssh.errorMsg();
- if( KMessageBox::Yes != messageBox(WarningYesNo, msg, caption) ) {
- closeConnection();
- error(ERR_USER_CANCELED, TQString::null);
- return;
- }
- ssh.acceptHostKey(true);
- break;
-
- case KSshProcess::ERR_DIFF_HOST_KEY:
- caption = i18n("Warning: Host's identity changed.");
- msg = ssh.errorMsg();
- if( KMessageBox::Yes != messageBox(WarningYesNo, msg, caption) ) {
- closeConnection();
- error(ERR_USER_CANCELED, TQString::null);
- return;
- }
- ssh.acceptHostKey(true);
- break;
-
- case KSshProcess::ERR_AUTH_FAILED:
- infoMessage(i18n("Authentication failed."));
- error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
- return;
-
- case KSshProcess::ERR_AUTH_FAILED_NEW_KEY:
- msg = ssh.errorMsg();
- error(ERR_COULD_NOT_LOGIN, msg);
- return;
-
- case KSshProcess::ERR_AUTH_FAILED_DIFF_KEY:
- msg = ssh.errorMsg();
- error(ERR_COULD_NOT_LOGIN, msg);
- return;
-
- case KSshProcess::ERR_CLOSED_BY_REMOTE_HOST:
- infoMessage(i18n("Connection failed."));
- caption = i18n("Connection closed by remote host.");
- msg = ssh.errorMsg();
- messageBox(Information, msg, caption);
- closeConnection();
- error(ERR_COULD_NOT_LOGIN, msg);
- return;
-
- case KSshProcess::ERR_INTERACT:
- case KSshProcess::ERR_INTERNAL:
- case KSshProcess::ERR_UNKNOWN:
- case KSshProcess::ERR_INVALID_STATE:
- case KSshProcess::ERR_CANNOT_LAUNCH:
- case KSshProcess::ERR_HOST_KEY_REJECTED:
- default:
- infoMessage(i18n("Connection failed."));
- caption = i18n("Unexpected SFTP error: %1").arg(err);
- msg = ssh.errorMsg();
- messageBox(Information, msg, caption);
- closeConnection();
- error(ERR_UNKNOWN, msg);
- return;
- }
- }
+sftpProtocol::sftpProtocol(const TQCString &pool_socket, const TQCString &app_socket)
+ : SlaveBase("tdeio_sftp", pool_socket, app_socket),
+ mConnected(false), mPort(-1), mSession(NULL), mSftp(NULL) {
+#ifndef Q_WS_WIN
+ kdDebug(TDEIO_SFTP_DB) << "pid = " << getpid() << endl;
- // catch all in case we did something wrong above
- if( !mConnected ) {
- error(ERR_INTERNAL, TQString::null);
- return;
- }
+ kdDebug(TDEIO_SFTP_DB) << "debug = " << getenv("TDEIO_SFTP_LOG_VERBOSITY") << endl;
+#endif
- // Now send init packet.
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): Sending SSH2_FXP_INIT packet." << endl;
- TQByteArray p;
- TQDataStream packet(p, IO_WriteOnly);
- packet << (TQ_UINT32)5; // packet length
- packet << (TQ_UINT8) SSH2_FXP_INIT; // packet type
- packet << (TQ_UINT32)SSH2_FILEXFER_VERSION; // client version
-
- putPacket(p);
- getPacket(p);
-
- TQDataStream s(p, IO_ReadOnly);
- TQ_UINT32 version;
- TQ_UINT8 type;
- s >> type;
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): Got type " << type << endl;
-
- if( type == SSH2_FXP_VERSION ) {
- s >> version;
- kdDebug(TDEIO_SFTP_DB) << "openConnection(): Got server version " << version << endl;
-
- // XXX Get extensions here
- sftpVersion = version;
-
- /* Server should return lowest common version supported by
- * client and server, but double check just in case.
- */
- if( sftpVersion > SSH2_FILEXFER_VERSION ) {
- error(ERR_UNSUPPORTED_PROTOCOL,
- i18n("SFTP version %1").arg(version));
- closeConnection();
- return;
- }
- }
- else {
- error(ERR_UNKNOWN, i18n("Protocol error."));
- closeConnection();
- return;
- }
+ mCallbacks = (ssh_callbacks) malloc(sizeof(struct ssh_callbacks_struct));
+ if (mCallbacks == NULL) {
+ error(TDEIO::ERR_OUT_OF_MEMORY, i18n("Could not allocate callbacks"));
+ return;
+ }
+ ZERO_STRUCTP(mCallbacks);
- // Login succeeded!
- infoMessage(i18n("Successfully connected to %1").arg(mHost));
- info.url.setProtocol("sftp");
- info.url.setHost(mHost);
- info.url.setPort(mPort);
- info.url.setUser(mUsername);
- info.username = mUsername;
- info.password = mPassword;
- kdDebug(TDEIO_SFTP_DB) << "sftpProtocol(): caching info.username = " << info.username <<
- ", info.url = " << info.url.prettyURL() << endl;
- cacheAuthentication(info);
- mConnected = true;
- connected();
-
- mPassword.fill('x');
- info.password.fill('x');
+ mCallbacks->userdata = this;
+ mCallbacks->auth_function = ::auth_callback;
+ if (getenv("TDEIO_SFTP_LOG_VERBOSITY")) {
+ mCallbacks->log_function = ::log_callback;
+ }
- return;
+ ssh_callbacks_init(mCallbacks);
}
-void sftpProtocol::closeConnection() {
- kdDebug(TDEIO_SFTP_DB) << "closeConnection()" << endl;
- ssh.disconnect();
- mConnected = false;
-}
+sftpProtocol::~sftpProtocol() {
+#ifndef Q_WS_WIN
+ kdDebug(TDEIO_SFTP_DB) << "pid = " << getpid() << endl;
+#endif
+ closeConnection();
-void sftpProtocol::sftpCopyPut(const KURL& src, const KURL& dest, int permissions, bool overwrite) {
+ delete mCallbacks;
- KDE_struct_stat buff;
- TQCString file (TQFile::encodeName(src.path()));
+ /* cleanup and shut down cryto stuff */
+ ssh_finalize();
+}
- if (KDE_lstat(file.data(), &buff) == -1) {
- error (ERR_DOES_NOT_EXIST, src.prettyURL());
- return;
- }
+void sftpProtocol::setHost(const TQString& h, int port, const TQString& user, const TQString& pass) {
+ kdDebug(TDEIO_SFTP_DB) << "setHost(): " << user << "@" << h << ":" << port << endl;
- if (S_ISDIR (buff.st_mode)) {
- error (ERR_IS_DIRECTORY, src.prettyURL());
- return;
- }
+ if (mConnected) {
+ closeConnection();
+ }
- int fd = KDE_open (file.data(), O_RDONLY);
- if (fd == -1) {
- error (ERR_CANNOT_OPEN_FOR_READING, src.prettyURL());
- return;
- }
+ mHost = h;
- totalSize (buff.st_size);
+ if (port > 0) {
+ mPort = port;
+ } else {
+ struct servent *pse;
+ if ((pse = getservbyname("ssh", "tcp") ) == NULL) {
+ mPort = 22;
+ } else {
+ mPort = ntohs(pse->s_port);
+ }
+ }
- sftpPut (dest, permissions, false, overwrite, fd);
+ kdDebug(TDEIO_SFTP_DB) << "setHost(): mPort=" << mPort << endl;
- // Close the file descriptor...
- ::close( fd );
+ mUsername = user;
+ mPassword = pass;
}
-void sftpProtocol::sftpPut( const KURL& dest, int permissions, bool resume, bool overwrite, int fd ) {
-
- openConnection();
- if( !mConnected )
- return;
-
- kdDebug(TDEIO_SFTP_DB) << "sftpPut(): " << dest
- << ", resume=" << resume
- << ", overwrite=" << overwrite << endl;
-
- KURL origUrl( dest );
- sftpFileAttr origAttr(remoteEncoding());
- bool origExists = false;
-
- // Stat original (without part ext) to see if it already exists
- int code = sftpStat(origUrl, origAttr);
-
- if( code == SSH2_FX_OK ) {
- kdDebug(TDEIO_SFTP_DB) << "sftpPut(): <file> already exists" << endl;
+void sftpProtocol::openConnection() {
- // Delete remote file if its size is zero
- if( origAttr.fileSize() == 0 ) {
- if( sftpRemove(origUrl, true) != SSH2_FX_OK ) {
- error(ERR_CANNOT_DELETE_ORIGINAL, origUrl.prettyURL());
- return;
- }
- }
- else {
- origExists = true;
- }
- }
- else if( code != SSH2_FX_NO_SUCH_FILE ) {
- processStatus(code, origUrl.prettyURL());
- return;
- }
+ if (mConnected) {
+ return;
+ }
- // Do not waste time/resources with more remote stat calls if the file exists
- // and we weren't instructed to overwrite it...
- if( origExists && !overwrite ) {
- error(ERR_FILE_ALREADY_EXIST, origUrl.prettyURL());
- return;
- }
+ kdDebug(TDEIO_SFTP_DB) << "username=" << mUsername << ", host=" << mHost << ", port=" << mPort << endl;
- // Stat file with part ext to see if it already exists...
- KURL partUrl( origUrl );
- partUrl.setFileName( partUrl.fileName() + ".part" );
-
- TQ_UINT64 offset = 0;
- bool partExists = false;
- bool markPartial = config()->readBoolEntry("MarkPartial", true);
-
- if( markPartial ) {
-
- sftpFileAttr partAttr(remoteEncoding());
- code = sftpStat(partUrl, partAttr);
-
- if( code == SSH2_FX_OK ) {
- kdDebug(TDEIO_SFTP_DB) << "sftpPut(): .part file already exists" << endl;
- partExists = true;
- offset = partAttr.fileSize();
-
- // If for some reason, both the original and partial files exist,
- // skip resumption just like we would if the size of the partial
- // file is zero...
- if( origExists || offset == 0 )
- {
- if( sftpRemove(partUrl, true) != SSH2_FX_OK ) {
- error(ERR_CANNOT_DELETE_PARTIAL, partUrl.prettyURL());
- return;
- }
-
- if( sftpRename(origUrl, partUrl) != SSH2_FX_OK ) {
- error(ERR_CANNOT_RENAME_ORIGINAL, origUrl.prettyURL());
- return;
- }
-
- offset = 0;
- }
- else if( !overwrite && !resume ) {
- if (fd != -1)
- resume = (KDE_lseek(fd, offset, SEEK_SET) != -1);
- else
- resume = canResume( offset );
-
- kdDebug(TDEIO_SFTP_DB) << "sftpPut(): can resume = " << resume
- << ", offset = " << offset;
-
- if( !resume ) {
- error(ERR_FILE_ALREADY_EXIST, partUrl.prettyURL());
- return;
- }
- }
- else {
- offset = 0;
- }
- }
- else if( code == SSH2_FX_NO_SUCH_FILE ) {
- if( origExists && sftpRename(origUrl, partUrl) != SSH2_FX_OK ) {
- error(ERR_CANNOT_RENAME_ORIGINAL, origUrl.prettyURL());
- return;
- }
- }
- else {
- processStatus(code, partUrl.prettyURL());
- return;
- }
- }
+ infoMessage(i18n("Opening SFTP connection to host %1:%2").arg(mHost).arg(mPort));
- // Determine the url we will actually write to...
- KURL writeUrl (markPartial ? partUrl:origUrl);
-
- TQ_UINT32 pflags = 0;
- if( overwrite && !resume )
- pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_TRUNC;
- else if( !overwrite && !resume )
- pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_EXCL;
- else if( overwrite && resume )
- pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT;
- else if( !overwrite && resume )
- pflags = SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_APPEND;
-
- sftpFileAttr attr(remoteEncoding());
- TQByteArray handle;
-
- // Set the permissions of the file we write to if it didn't already exist
- // and the permission info is supplied, i.e it is not -1
- if( !partExists && !origExists && permissions != -1)
- attr.setPermissions(permissions);
-
- code = sftpOpen( writeUrl, pflags, attr, handle );
- if( code != SSH2_FX_OK ) {
-
- // Rename the file back to its original name if a
- // put fails due to permissions problems...
- if( markPartial && overwrite ) {
- (void) sftpRename(partUrl, origUrl);
- writeUrl = origUrl;
- }
+ if (mHost.isEmpty()) {
+ kdDebug(TDEIO_SFTP_DB) << "openConnection(): Need hostname..." << endl;
+ error(TDEIO::ERR_UNKNOWN_HOST, i18n("No hostname specified."));
+ return;
+ }
- if( code == SSH2_FX_FAILURE ) { // assume failure means file exists
- error(ERR_FILE_ALREADY_EXIST, writeUrl.prettyURL());
- return;
- }
- else {
- processStatus(code, writeUrl.prettyURL());
- return;
- }
+ // Setup AuthInfo for use with password caching and the
+ // password dialog box.
+ AuthInfo info;
+
+ info.url.setProtocol("sftp");
+ info.url.setHost(mHost);
+ info.url.setPort(mPort);
+ info.url.setUser(mUsername);
+ info.caption = i18n("SFTP Login");
+ info.comment = "sftp://" + mHost + ':' + TQString::number(mPort);
+ info.commentLabel = i18n("site:");
+ info.username = mUsername;
+ info.keepPassword = true; // make the "keep Password" check box visible to the user.
+
+ // Check for cached authentication info if no password is specified...
+ if (mPassword.isEmpty()) {
+ kdDebug(TDEIO_SFTP_DB) << "checking cache: info.username = " << info.username
+ << ", info.url = " << info.url.prettyURL() << endl;
+
+ if (checkCachedAuthentication(info)) {
+ mUsername = info.username;
+ mPassword = info.password;
}
+ }
- long nbytes;
- TQByteArray buff;
-
- do {
-
- if( fd != -1 ) {
- buff.resize( 16*1024 );
- if ( (nbytes = ::read(fd, buff.data(), buff.size())) > -1 )
- buff.resize( nbytes );
- }
- else {
- dataReq();
- nbytes = readData( buff );
- }
+ // Start the ssh connection.
+ TQString msg; // msg for dialog box
+ TQString caption; // dialog box caption
+ unsigned char *hash = NULL; // the server hash
+ char *hexa;
+ char *verbosity;
+ int rc, state;
+ int timeout_sec = 30, timeout_usec = 0;
+
+ mSession = ssh_new();
+ if (mSession == NULL) {
+ error(TDEIO::ERR_INTERNAL, i18n("Could not create a new SSH session."));
+ return;
+ }
- if( nbytes >= 0 ) {
- if( (code = sftpWrite(handle, offset, buff)) != SSH2_FX_OK ) {
- error(ERR_COULD_NOT_WRITE, dest.prettyURL());
- return;
- }
-
- offset += nbytes;
- processedSize(offset);
-
- /* Check if slave was killed. According to slavebase.h we
- * need to leave the slave methods as soon as possible if
- * the slave is killed. This allows the slave to be cleaned
- * up properly.
- */
- if( wasKilled() ) {
- sftpClose(handle);
- closeConnection();
- error(ERR_UNKNOWN, i18n("An internal error occurred. Please try again."));
- return;
- }
- }
+ kdDebug(TDEIO_SFTP_DB) << "Creating the SSH session and setting options" << endl;
- } while( nbytes > 0 );
+ // Set timeout
+ rc = ssh_options_set(mSession, SSH_OPTIONS_TIMEOUT, &timeout_sec);
+ if (rc < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not set a timeout.";
+ }
+ rc = ssh_options_set(mSession, SSH_OPTIONS_TIMEOUT_USEC, &timeout_usec);
+ if (rc < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not set a timeout in usec.";
+ }
- if( nbytes < 0 ) {
- sftpClose(handle);
+ // Don't use any compression
+ rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_C_S, "none");
+ if (rc < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not set compression client <- server.";
+ }
- if( markPartial ) {
- // Remove remote file if it smaller than our keep size
- uint minKeepSize = config()->readNumEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
+ rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_S_C, "none");
+ if (rc < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not set compression server -> client.";
+ }
- if( sftpStat(writeUrl, attr) == SSH2_FX_OK ) {
- if( attr.fileSize() < minKeepSize ) {
- sftpRemove(writeUrl, true);
- }
- }
- }
+ // Set host and port
+ rc = ssh_options_set(mSession, SSH_OPTIONS_HOST, mHost.utf8().data());
+ if (rc < 0) {
+ error(TDEIO::ERR_OUT_OF_MEMORY, i18n("Could not set host."));
+ return;
+ }
- error( ERR_UNKNOWN, i18n("Unknown error was encountered while copying the file "
- "to '%1'. Please try again.").arg(dest.host()) );
- return;
+ if (mPort > 0) {
+ rc = ssh_options_set(mSession, SSH_OPTIONS_PORT, &mPort);
+ if (rc < 0) {
+ error(TDEIO::ERR_OUT_OF_MEMORY, i18n("Could not set port."));
+ return;
}
+ }
- if( (code = sftpClose(handle)) != SSH2_FX_OK ) {
- error(ERR_COULD_NOT_WRITE, writeUrl.prettyURL());
- return;
+ // Set the username
+ if (!mUsername.isEmpty()) {
+ rc = ssh_options_set(mSession, SSH_OPTIONS_USER, mUsername.utf8().data());
+ if (rc < 0) {
+ error(TDEIO::ERR_OUT_OF_MEMORY, i18n("Could not set username."));
+ return;
}
+ }
- // If wrote to a partial file, then remove the part ext
- if( markPartial ) {
- if( sftpRename(partUrl, origUrl) != SSH2_FX_OK ) {
- error(ERR_CANNOT_RENAME_PARTIAL, origUrl.prettyURL());
- return;
- }
+ verbosity = getenv("TDEIO_SFTP_LOG_VERBOSITY");
+ if (verbosity) {
+ rc = ssh_options_set(mSession, SSH_OPTIONS_LOG_VERBOSITY_STR, verbosity);
+ if (rc < 0) {
+ error(TDEIO::ERR_OUT_OF_MEMORY, i18n("Could not set log verbosity."));
+ return;
}
+ }
- finished();
-}
-
-void sftpProtocol::put ( const KURL& url, int permissions, bool overwrite, bool resume ){
- kdDebug(TDEIO_SFTP_DB) << "put(): " << url << ", overwrite = " << overwrite
- << ", resume = " << resume << endl;
-
- sftpPut( url, permissions, resume, overwrite );
-}
+ // Read ~/.ssh/config
+ rc = ssh_options_parse_config(mSession, NULL);
+ if (rc < 0) {
+ error(TDEIO::ERR_INTERNAL, i18n("Could not parse the config file."));
+ return;
+ }
-void sftpProtocol::stat ( const KURL& url ){
- kdDebug(TDEIO_SFTP_DB) << "stat(): " << url << endl;
+ ssh_set_callbacks(mSession, mCallbacks);
- openConnection();
- if( !mConnected )
- return;
+ kdDebug(TDEIO_SFTP_DB) << "Trying to connect to the SSH server" << endl;
- // If the stat URL has no path, do not attempt to determine the real
- // path and do a redirect. KRun will simply ignore such requests.
- // Instead, simply return the mime-type as a directory...
- if( !url.hasPath() ) {
- UDSEntry entry;
- UDSAtom atom;
+ /* try to connect */
+ rc = ssh_connect(mSession);
+ if (rc < 0) {
+ error(TDEIO::ERR_COULD_NOT_CONNECT, TQString::fromUtf8(ssh_get_error(mSession)));
+ closeConnection();
+ return;
+ }
- atom.m_uds = TDEIO::UDS_NAME;
- atom.m_str = TQString::null;
- entry.append( atom );
+ kdDebug(TDEIO_SFTP_DB) << "Getting the SSH server hash" << endl;
- atom.m_uds = TDEIO::UDS_FILE_TYPE;
- atom.m_long = S_IFDIR;
- entry.append( atom );
+ /* get the hash */
+ ssh_key serverKey;
+ if (ssh_get_server_publickey(mSession, &serverKey) < 0) {
+ error(TDEIO::ERR_COULD_NOT_CONNECT, TQString::fromUtf8(ssh_get_error(mSession)));
+ closeConnection();
+ return;
+ }
- atom.m_uds = TDEIO::UDS_ACCESS;
- atom.m_long = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
- entry.append( atom );
+ size_t hlen;
+ if (ssh_get_publickey_hash(serverKey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hlen) < 0) {
+ error(TDEIO::ERR_COULD_NOT_CONNECT, TQString::fromUtf8(ssh_get_error(mSession)));
+ closeConnection();
+ return;
+ }
- atom.m_uds = TDEIO::UDS_USER;
- atom.m_str = mUsername;
- entry.append( atom );
- atom.m_uds = TDEIO::UDS_GROUP;
- entry.append( atom );
+ kdDebug(TDEIO_SFTP_DB) << "Checking if the SSH server is known" << endl;
- // no size
- statEntry( entry );
- finished();
+ /* check the server public key hash */
+ state = ssh_session_is_known_server(mSession);
+ switch (state) {
+ case SSH_KNOWN_HOSTS_OK:
+ break;
+ case SSH_KNOWN_HOSTS_OTHER:
+ delete hash;
+ error(TDEIO::ERR_CONNECTION_BROKEN, i18n("The host key for this server was "
+ "not found, but another type of key exists.\n"
+ "An attacker might change the default server key to confuse your "
+ "client into thinking the key does not exist.\n"
+ "Please contact your system administrator.\n%1").arg(TQString::fromUtf8(ssh_get_error(mSession))));
+ closeConnection();
+ return;
+ case SSH_SERVER_KNOWN_CHANGED:
+ hexa = ssh_get_hexa(hash, hlen);
+ delete hash;
+ /* TODO print known_hosts file, port? */
+ error(TDEIO::ERR_CONNECTION_BROKEN, i18n("The host key for the server %1 has changed.\n"
+ "This could either mean that DNS SPOOFING is happening or the IP "
+ "address for the host and its host key have changed at the same time.\n"
+ "The fingerprint for the key sent by the remote host is:\n %2\n"
+ "Please contact your system administrator.\n%3").arg(
+ mHost).arg(TQString::fromUtf8(hexa)).arg(TQString::fromUtf8(ssh_get_error(mSession))));
+ delete hexa;
+ closeConnection();
+ return;
+ case SSH_KNOWN_HOSTS_NOT_FOUND:
+ case SSH_KNOWN_HOSTS_UNKNOWN:
+ hexa = ssh_get_hexa(hash, hlen);
+ delete hash;
+ caption = i18n("Warning: Cannot verify host's identity.");
+ msg = i18n("The authenticity of host %1 cannot be established.\n"
+ "The key fingerprint is: %2\n"
+ "Are you sure you want to continue connecting?").arg(mHost).arg(hexa);
+ delete hexa;
+
+ if (KMessageBox::Yes != messageBox(WarningYesNo, msg, caption)) {
+ closeConnection();
+ error(TDEIO::ERR_USER_CANCELED, TQString());
return;
- }
+ }
- int code;
- sftpFileAttr attr(remoteEncoding());
- if( (code = sftpStat(url, attr)) != SSH2_FX_OK ) {
- processStatus(code, url.prettyURL());
+ /* write the known_hosts file */
+ kdDebug(TDEIO_SFTP_DB) << "Adding server to known_hosts file." << endl;
+ if (ssh_session_update_known_hosts(mSession) != SSH_OK) {
+ error(TDEIO::ERR_USER_CANCELED, TQString::fromUtf8(ssh_get_error(mSession)));
+ closeConnection();
return;
- }
- else {
- //kdDebug() << "We sent and received stat packet ok" << endl;
- attr.setFilename(url.fileName());
- statEntry(attr.entry());
- }
+ }
+ break;
+ case SSH_KNOWN_HOSTS_ERROR:
+ delete hash;
+ error(TDEIO::ERR_COULD_NOT_CONNECT, TQString::fromUtf8(ssh_get_error(mSession)));
+ return;
+ }
- finished();
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with the server" << endl;
- kdDebug(TDEIO_SFTP_DB) << "stat: END" << endl;
+ // Try to authenticate
+ rc = ssh_userauth_none(mSession, NULL);
+ if (rc == SSH_AUTH_ERROR) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
return;
-}
-
+ }
-void sftpProtocol::mimetype ( const KURL& url ){
- kdDebug(TDEIO_SFTP_DB) << "mimetype(): " << url << endl;
+ int method = ssh_auth_list(mSession);
+ bool firstTime = true;
+ bool dlgResult;
+ while (rc != SSH_AUTH_SUCCESS) {
- openConnection();
- if( !mConnected )
- return;
-
- TQ_UINT32 pflags = SSH2_FXF_READ;
- TQByteArray handle, mydata;
- sftpFileAttr attr(remoteEncoding());
- int code;
- if( (code = sftpOpen(url, pflags, attr, handle)) != SSH2_FX_OK ) {
- error(ERR_CANNOT_OPEN_FOR_READING, url.prettyURL());
+ // Try to authenticate with public key first
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate public key" << endl;
+ if (method & SSH_AUTH_METHOD_PUBLICKEY) {
+ rc = ssh_userauth_autopubkey(mSession, NULL);
+ if (rc == SSH_AUTH_ERROR) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
return;
+ } else if (rc == SSH_AUTH_SUCCESS) {
+ break;
+ }
}
- TQ_UINT32 len = 1024; // Get first 1k for determining mimetype
- TQ_UINT64 offset = 0;
- code = SSH2_FX_OK;
- while( offset < len && code == SSH2_FX_OK ) {
- if( (code = sftpRead(handle, offset, len, mydata)) == SSH2_FX_OK ) {
- data(mydata);
- offset += mydata.size();
- processedSize(offset);
-
- kdDebug(TDEIO_SFTP_DB) << "mimetype(): offset = " << offset << endl;
- }
+ info.caption = i18n("SFTP Login");
+ info.readOnly = false;
+ if (firstTime) {
+ info.prompt = i18n("Please enter your username and password.");
+ } else {
+ info.prompt = i18n("Login failed.\nPlease confirm your username and password, and enter them again.");
+ }
+ dlgResult = openPassDlg(info);
+
+ // Handle user canceled or dialog failed to open...
+ if (!dlgResult) {
+ kdDebug(TDEIO_SFTP_DB) << "User canceled, dlgResult = " << dlgResult << endl;
+ closeConnection();
+ error(TDEIO::ERR_USER_CANCELED, TQString());
+ return;
}
+ firstTime = false;
- data(TQByteArray());
- processedSize(offset);
- sftpClose(handle);
- finished();
- kdDebug(TDEIO_SFTP_DB) << "mimetype(): END" << endl;
-}
-
-
-void sftpProtocol::listDir(const KURL& url) {
- kdDebug(TDEIO_SFTP_DB) << "listDir(): " << url << endl;
-
- openConnection();
- if( !mConnected )
- return;
-
- if( !url.hasPath() ) {
- KURL newUrl ( url );
- if( sftpRealPath(url, newUrl) == SSH2_FX_OK ) {
- kdDebug(TDEIO_SFTP_DB) << "listDir: Redirecting to " << newUrl << endl;
- redirection(newUrl);
- finished();
- return;
- }
+ if (mUsername != info.username) {
+ kdDebug(TDEIO_SFTP_DB) << "Username changed from " << mUsername
+ << " to " << info.username << endl;
}
+ mUsername = info.username;
+ mPassword = info.password;
- int code;
- TQByteArray handle;
-
- if( (code = sftpOpenDirectory(url, handle)) != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "listDir(): open directory failed" << endl;
- processStatus(code, url.prettyURL());
+ // Try to authenticate with keyboard interactive
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with keyboard interactive" << endl;
+ if (method & SSH_AUTH_METHOD_INTERACTIVE) {
+ rc = authenticateKeyboardInteractive(info);
+ if (rc == SSH_AUTH_ERROR) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
return;
+ } else if (rc == SSH_AUTH_SUCCESS) {
+ break;
+ }
}
-
- code = SSH2_FX_OK;
- while( code == SSH2_FX_OK ) {
- code = sftpReadDir(handle, url);
- if( code != SSH2_FX_OK && code != SSH2_FX_EOF )
- processStatus(code, url.prettyURL());
- kdDebug(TDEIO_SFTP_DB) << "listDir(): return code = " << code << endl;
- }
-
- if( (code = sftpClose(handle)) != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "listdir(): closing of directory failed" << endl;
- processStatus(code, url.prettyURL());
+ // Try to authenticate with password
+ kdDebug(TDEIO_SFTP_DB) << "Trying to authenticate with password" << endl;
+ if (method & SSH_AUTH_METHOD_PASSWORD) {
+ rc = ssh_userauth_password(mSession, mUsername.utf8().data(),
+ mPassword.utf8().data());
+ if (rc == SSH_AUTH_ERROR) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
return;
+ } else if (rc == SSH_AUTH_SUCCESS) {
+ break;
+ }
}
+ }
- finished();
- kdDebug(TDEIO_SFTP_DB) << "listDir(): END" << endl;
-}
+ // start sftp session
+ kdDebug(TDEIO_SFTP_DB) << "Trying to request the sftp session" << endl;
+ mSftp = sftp_new(mSession);
+ if (mSftp == NULL) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Unable to request the SFTP subsystem. "
+ "Make sure SFTP is enabled on the server."));
+ return;
+ }
-/** Make a directory.
- OpenSSH does not follow the internet draft for sftp in this case.
- The format of the mkdir request expected by OpenSSH sftp server is:
- uint32 id
- string path
- ATTR attr
- */
-void sftpProtocol::mkdir(const KURL&url, int permissions){
+ kdDebug(TDEIO_SFTP_DB) << "Trying to initialize the sftp session" << endl;
+ if (sftp_init(mSftp) < 0) {
+ closeConnection();
+ error(TDEIO::ERR_COULD_NOT_LOGIN, i18n("Could not initialize the SFTP session."));
+ return;
+ }
- kdDebug(TDEIO_SFTP_DB) << "mkdir() creating dir: " << url.path() << endl;
+ // Login succeeded!
+ infoMessage(i18n("Successfully connected to %1").arg(mHost));
+ info.url.setProtocol("sftp");
+ info.url.setHost(mHost);
+ info.url.setPort(mPort);
+ info.url.setUser(mUsername);
+ info.username = mUsername;
+ info.password = mPassword;
- openConnection();
- if( !mConnected )
- return;
+ kdDebug(TDEIO_SFTP_DB) << "Caching info.username = " << info.username
+ << ", info.url = " << info.url.prettyURL() << endl;
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
+ cacheAuthentication(info);
- sftpFileAttr attr(remoteEncoding());
+ //setTimeoutSpecialCommand(TDEIO_SFTP_SPECIAL_TIMEOUT);
- if (permissions != -1)
- attr.setPermissions(permissions);
+ mConnected = true;
+ connected();
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
+ mPassword.fill('x');
+ mPassword = "";
+ info.password.fill('x');
+ info.password = "";
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << TQ_UINT32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len + attr.size());
- s << (TQ_UINT8)SSH2_FXP_MKDIR;
- s << id;
- s.writeBytes(path.data(), len);
- s << attr;
+ return;
+}
- kdDebug(TDEIO_SFTP_DB) << "mkdir(): packet size is " << p.size() << endl;
+void sftpProtocol::closeConnection() {
+ kdDebug(TDEIO_SFTP_DB) << "closeConnection()" << endl;
- putPacket(p);
- getPacket(p);
+ sftp_free(mSftp);
+ mSftp = NULL;
- TQ_UINT8 type;
- TQDataStream r(p, IO_ReadOnly);
+ ssh_disconnect(mSession);
+ mSession = NULL;
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "mkdir: sftp packet id mismatch" << endl;
- error(ERR_COULD_NOT_MKDIR, path);
- finished();
- return;
- }
+ mConnected = false;
+}
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "mkdir(): unexpected packet type of " << type << endl;
- error(ERR_COULD_NOT_MKDIR, path);
- finished();
- return;
- }
+#if 0
+void sftpProtocol::special(const TQByteArray &data) {
+ int rc;
+ kdDebug(TDEIO_SFTP_DB) << "special(): polling";
+
+ /*
+ * channel_poll() returns the number of bytes that may be read on the
+ * channel. It does so by checking the input buffer and eventually the
+ * network socket for data to read. If the input buffer is not empty, it
+ * will not probe the network (and such not read packets nor reply to
+ * keepalives).
+ *
+ * As channel_poll can act on two specific buffers (a channel has two
+ * different stream: stdio and stderr), polling for data on the stderr
+ * stream has more chance of not being in the problematic case (data left
+ * in the buffer). Checking the return value (for >0) would be a good idea
+ * to debug the problem.
+ */
+ rc = channel_poll(mSftp->channel, 0);
+ if (rc > 0) {
+ rc = channel_poll(mSftp->channel, 1);
+ }
+
+ if (rc < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "channel_poll failed: " << ssh_get_error(mSession);
+ }
+
+ setTimeoutSpecialCommand(TDEIO_SFTP_SPECIAL_TIMEOUT);
+}
+#endif
- int code;
- r >> code;
- if( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "mkdir(): failed with code " << code << endl;
-
- // Check if mkdir failed because the directory already exists so that
- // we can return the appropriate message...
- sftpFileAttr dirAttr(remoteEncoding());
- if ( sftpStat(url, dirAttr) == SSH2_FX_OK )
- {
- error( ERR_DIR_ALREADY_EXIST, url.prettyURL() );
- return;
- }
+void sftpProtocol::statMime(const KURL &url) {
+ kdDebug(TDEIO_SFTP_DB) << "stat: " << url.url() << endl;
- error(ERR_COULD_NOT_MKDIR, path);
- }
+ openConnection();
+ if (!mConnected) {
+ error(TDEIO::ERR_CONNECTION_BROKEN, url.prettyURL());
+ return;
+ }
- finished();
-}
+ const TQString path = url.path();
+ const TQByteArray path_c = path.utf8();
-void sftpProtocol::rename(const KURL& src, const KURL& dest, bool overwrite){
- kdDebug(TDEIO_SFTP_DB) << "rename(" << src << " -> " << dest << ")" << endl;
+ sftp_attributes sb = sftp_lstat(mSftp, path_c.data());
+ if (sb == NULL) {
+ reportError(url, sftp_get_error(mSftp));
+ return;
+ }
- if (!isSupportedOperation(SSH2_FXP_RENAME)) {
- error(ERR_UNSUPPORTED_ACTION,
- i18n("The remote host does not support renaming files."));
+ switch (sb->type) {
+ case SSH_FILEXFER_TYPE_DIRECTORY:
+ sftp_attributes_free(sb);
+ emit mimeType("inode/directory");
return;
- }
+ case SSH_FILEXFER_TYPE_SPECIAL:
+ case SSH_FILEXFER_TYPE_UNKNOWN:
+ error(TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL());
+ sftp_attributes_free(sb);
+ return;
+ case SSH_FILEXFER_TYPE_SYMLINK:
+ case SSH_FILEXFER_TYPE_REGULAR:
+ break;
+ }
- openConnection();
- if( !mConnected )
- return;
+ size_t fileSize = sb->size;
+ sftp_attributes_free(sb);
- // Always stat the destination before attempting to rename
- // a file or a directory...
- sftpFileAttr attr(remoteEncoding());
- int code = sftpStat(dest, attr);
+ int flags = 0;
- // If the destination directory, exists tell it to the job
- // so it the proper action can be presented to the user...
- if( code == SSH2_FX_OK )
- {
- if (!overwrite)
- {
- if ( S_ISDIR(attr.permissions()) )
- error( TDEIO::ERR_DIR_ALREADY_EXIST, dest.url() );
- else
- error( TDEIO::ERR_FILE_ALREADY_EXIST, dest.url() );
- return;
- }
+ flags = O_RDONLY;
- // If overwrite is specified, then simply remove the existing file/dir first...
- if( (code = sftpRemove( dest, !S_ISDIR(attr.permissions()) )) != SSH2_FX_OK )
- {
- processStatus(code);
- return;
- }
- }
+ mOpenFile = sftp_open(mSftp, path_c.data(), flags, 0);
- // Do the renaming...
- if( (code = sftpRename(src, dest)) != SSH2_FX_OK ) {
- processStatus(code);
- return;
- }
+ if (mOpenFile == NULL) {
+ error(TDEIO::ERR_CANNOT_OPEN_FOR_READING, path);
+ return;
+ }
- finished();
- kdDebug(TDEIO_SFTP_DB) << "rename(): END" << endl;
+ // Determine the mimetype of the file to be retrieved, and emit it.
+ // This is mandatory in all slaves (for KRun/BrowserRun to work).
+ // If we're not opening the file ReadOnly or ReadWrite, don't attempt to
+ // read the file and send the mimetype.
+ size_t bytesRequested = 1024;
+ ssize_t bytesRead = 0;
+ TQByteArray buffer(bytesRequested);
+
+ bytesRead = sftp_read(mOpenFile, buffer.data(), bytesRequested);
+ if (bytesRead < 0) {
+ error(TDEIO::ERR_COULD_NOT_READ, mOpenUrl.prettyURL());
+ closeFile();
+ return;
+ } else {
+ TQByteArray fileData;
+ fileData.setRawData(buffer.data(), bytesRead);
+ KMimeMagicResult *p_mimeType = KMimeMagic::self()->findBufferFileType(fileData, mOpenUrl.fileName());
+ emit mimeType(p_mimeType->mimeType());
+ }
+
+ sftp_close(mOpenFile);
+
+ mOpenFile = NULL;
}
-void sftpProtocol::symlink(const TQString& target, const KURL& dest, bool overwrite){
- kdDebug(TDEIO_SFTP_DB) << "symlink()" << endl;
+#if 0
+void sftpProtocol::read(TDEIO::filesize_t bytes) {
+ kdDebug(TDEIO_SFTP_DB) << "read, offset = " << openOffset << ", bytes = " << bytes;
- if (!isSupportedOperation(SSH2_FXP_SYMLINK)) {
- error(ERR_UNSUPPORTED_ACTION,
- i18n("The remote host does not support creating symbolic links."));
- return;
- }
+ ASSERT(mOpenFile != NULL);
- openConnection();
- if( !mConnected )
- return;
+ TQVarLengthArray<char> buffer(bytes);
- int code;
- bool failed = false;
- if( (code = sftpSymLink(target, dest)) != SSH2_FX_OK ) {
- if( overwrite ) { // try to delete the destination
- sftpFileAttr attr(remoteEncoding());
- if( (code = sftpStat(dest, attr)) != SSH2_FX_OK ) {
- failed = true;
- }
- else {
- if( (code = sftpRemove(dest, !S_ISDIR(attr.permissions())) ) != SSH2_FX_OK ) {
- failed = true;
- }
- else {
- // XXX what if rename fails again? We have lost the file.
- // Maybe rename dest to a temporary name first? If rename is
- // successful, then delete?
- if( (code = sftpSymLink(target, dest)) != SSH2_FX_OK )
- failed = true;
- }
- }
- }
- else if( code == SSH2_FX_FAILURE ) {
- error(ERR_FILE_ALREADY_EXIST, dest.prettyURL());
- return;
- }
- else
- failed = true;
- }
+ ssize_t bytesRead = sftp_read(mOpenFile, buffer.data(), bytes);
+ ASSERT(bytesRead <= static_cast<ssize_t>(bytes));
- // What error code do we return? Code for the original symlink command
- // or for the last command or for both? The second one is implemented here.
- if( failed )
- processStatus(code);
+ if (bytesRead < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not read " << mOpenUrl;
+ error(TDEIO::ERR_COULD_NOT_READ, mOpenUrl.prettyURL());
+ close();
+ return;
+ }
- finished();
+ TQByteArray fileData = TQByteArray::fromRawData(buffer.data(), bytesRead);
+ data(fileData);
}
-void sftpProtocol::chmod(const KURL& url, int permissions){
- TQString perms;
- perms.setNum(permissions, 8);
- kdDebug(TDEIO_SFTP_DB) << "chmod(" << url << ", " << perms << ")" << endl;
+void sftpProtocol::write(const TQByteArray &data) {
+ kdDebug(TDEIO_SFTP_DB) << "write, offset = " << openOffset << ", bytes = " << data.size();
- openConnection();
- if( !mConnected )
- return;
+ ASSERT(mOpenFile != NULL);
- sftpFileAttr attr(remoteEncoding());
-
- if (permissions != -1)
- attr.setPermissions(permissions);
+ ssize_t bytesWritten = sftp_write(mOpenFile, data.data(), data.size());
+ if (bytesWritten < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Could not write to " << mOpenUrl;
+ error(TDEIO::ERR_COULD_NOT_WRITE, mOpenUrl.prettyURL());
+ close();
+ return;
+ }
- int code;
- if( (code = sftpSetStat(url, attr)) != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "chmod(): sftpSetStat failed with error " << code << endl;
- if( code == SSH2_FX_FAILURE )
- error(ERR_CANNOT_CHMOD, TQString::null);
- else
- processStatus(code, url.prettyURL());
- }
- finished();
+ written(bytesWritten);
}
+void sftpProtocol::seek(TDEIO::filesize_t offset) {
+ kdDebug(TDEIO_SFTP_DB) << "seek, offset = " << offset;
-void sftpProtocol::del(const KURL &url, bool isfile){
- kdDebug(TDEIO_SFTP_DB) << "del(" << url << ", " << (isfile?"file":"dir") << ")" << endl;
+ ASSERT(mOpenFile != NULL);
- openConnection();
- if( !mConnected )
- return;
+ if (sftp_seek64(mOpenFile, static_cast<uint64_t>(offset)) < 0) {
+ error(TDEIO::ERR_COULD_NOT_SEEK, mOpenUrl.path());
+ close();
+ }
- int code;
- if( (code = sftpRemove(url, isfile)) != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "del(): sftpRemove failed with error code " << code << endl;
- processStatus(code, url.prettyURL());
- }
- finished();
+ position(sftp_tell64(mOpenFile));
}
+#endif
-void sftpProtocol::slave_status() {
- kdDebug(TDEIO_SFTP_DB) << "slave_status(): connected to "
- << mHost << "? " << mConnected << endl;
+void sftpProtocol::closeFile() {
+ if (mOpenFile) {
+ sftp_close(mOpenFile);
- slaveStatus ((mConnected ? mHost : TQString::null), mConnected);
+ mOpenFile = NULL;
+ finished();
+ }
}
-bool sftpProtocol::getPacket(TQByteArray& msg) {
- TQByteArray buf(4096);
+void sftpProtocol::get(const KURL& url) {
+ kdDebug(TDEIO_SFTP_DB) << "get(): " << url.url() << endl;
- // Get the message length...
- ssize_t len = atomicio(ssh.stdioFd(), buf.data(), 4, true /*read*/);
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- if( len == 0 || len == -1 ) {
- kdDebug(TDEIO_SFTP_DB) << "getPacket(): read of packet length failed, ret = "
- << len << ", error =" << strerror(errno) << endl;
- closeConnection();
- error( ERR_CONNECTION_BROKEN, mHost);
- msg.resize(0);
- return false;
- }
+ TQByteArray path = url.path().utf8();
- uint msgLen;
- TQDataStream s(buf, IO_ReadOnly);
- s >> msgLen;
+ char buf[MAX_XFER_BUF_SIZE] = {0};
+ sftp_file file = NULL;
+ ssize_t bytesread = 0;
+ // time_t curtime = 0;
+ time_t lasttime = 0;
+ time_t starttime = 0;
+ ssize_t totalbytesread = 0;
- //kdDebug(TDEIO_SFTP_DB) << "getPacket(): Message size = " << msgLen << endl;
+ sftp_attributes sb = sftp_lstat(mSftp, path.data());
+ if (sb == NULL) {
+ reportError(url, sftp_get_error(mSftp));
+ return;
+ }
- msg.resize(0);
+ switch (sb->type) {
+ case SSH_FILEXFER_TYPE_DIRECTORY:
+ error(TDEIO::ERR_IS_DIRECTORY, url.prettyURL());
+ sftp_attributes_free(sb);
+ return;
+ case SSH_FILEXFER_TYPE_SPECIAL:
+ case SSH_FILEXFER_TYPE_UNKNOWN:
+ error(TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL());
+ sftp_attributes_free(sb);
+ return;
+ case SSH_FILEXFER_TYPE_SYMLINK:
+ case SSH_FILEXFER_TYPE_REGULAR:
+ break;
+ }
- TQBuffer b( msg );
- b.open( IO_WriteOnly );
+ // Open file
+ file = sftp_open(mSftp, path.data(), O_RDONLY, 0);
+ if (file == NULL) {
+ error( TDEIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyURL());
+ sftp_attributes_free(sb);
+ return;
+ }
- while( msgLen ) {
- len = atomicio(ssh.stdioFd(), buf.data(), kMin((uint)buf.size(), msgLen), true /*read*/);
+ // Determine the mimetype of the file to be retrieved, and emit it.
+ // This is mandatory in all slaves (for KRun/BrowserRun to work)
+ // In real "remote" slaves, this is usually done using findByNameAndContent
+ // after receiving some data. But we don't know how much data the mimemagic rules
+ // need, so for local files, better use findByUrl with localUrl=true.
+ KMimeType::Ptr mt = KMimeType::findByURL( url, sb->permissions, false /* remote URL */ );
+ emit mimeType( mt->name() ); // FIXME test me
+
+ kdDebug(TDEIO_SFTP_DB) << "Total size: " << TQString::number(sb->size) << endl;
+ // Set the total size
+ totalSize(sb->size);
+
+ const TQString resumeOffset = metaData(TQString("resume"));
+ if (!resumeOffset.isEmpty()) {
+ bool ok;
+ ssize_t offset = resumeOffset.toLong(&ok);
+ if (ok && (offset > 0) && ((unsigned long long) offset < sb->size))
+ {
+ if (sftp_seek64(file, offset) == 0) {
+ canResume();
+ totalbytesread = offset;
+ kdDebug(TDEIO_SFTP_DB) << "Resume offset: " << TQString::number(offset) << endl;
+ }
+ }
+ }
- if( len == 0 || len == -1) {
- TQString errmsg;
- if (len == 0)
- errmsg = i18n("Connection closed");
- else
- errmsg = i18n("Could not read SFTP packet");
- kdDebug(TDEIO_SFTP_DB) << "getPacket(): nothing to read, ret = " <<
- len << ", error =" << strerror(errno) << endl;
- closeConnection();
- error(ERR_CONNECTION_BROKEN, errmsg);
- b.close();
- return false;
- }
+ if (file != NULL) {
+ bool isFirstPacket = true;
+ lasttime = starttime = time(NULL);
+
+ for (;;) {
+ bytesread = sftp_read(file, buf, MAX_XFER_BUF_SIZE);
+ kdDebug(TDEIO_SFTP_DB) << "bytesread=" << TQString::number(bytesread) << endl;
+ if (bytesread == 0) {
+ // All done reading
+ break;
+ } else if (bytesread < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Failed to read";
+ error(TDEIO::ERR_COULD_NOT_READ, url.prettyURL());
+ sftp_attributes_free(sb);
+ return;
+ }
- b.writeBlock(buf.data(), len);
+ TQByteArray filedata;
+ filedata.setRawData(buf, bytesread);
+ if (isFirstPacket) {
+ KMimeMagicResult *p_mimeType = KMimeMagic::self()->findBufferFileType(filedata, mOpenUrl.fileName());
+ mimeType(p_mimeType->mimeType());
+ kdDebug(TDEIO_SFTP_DB) << "mimetype=" << p_mimeType->mimeType() << endl;
+ isFirstPacket = false;
+ }
+ data(filedata);
+ filedata.resetRawData(buf, bytesread);
- //kdDebug(TDEIO_SFTP_DB) << "getPacket(): Read Message size = " << len << endl;
- //kdDebug(TDEIO_SFTP_DB) << "getPacket(): Copy Message size = " << msg.size() << endl;
+ // increment total bytes read
+ totalbytesread += bytesread;
- msgLen -= len;
+ processedSize(totalbytesread);
}
- b.close();
-
- return true;
-}
-
-/** Send an sftp packet to stdin of the ssh process. */
-bool sftpProtocol::putPacket(TQByteArray& p){
-// kdDebug(TDEIO_SFTP_DB) << "putPacket(): size == " << p.size() << endl;
- int ret;
- ret = atomicio(ssh.stdioFd(), p.data(), p.size(), false /*write*/);
- if( ret <= 0 ) {
- kdDebug(TDEIO_SFTP_DB) << "putPacket(): write failed, ret =" << ret <<
- ", error = " << strerror(errno) << endl;
- return false;
- }
+ kdDebug(TDEIO_SFTP_DB) << "size processed=" << totalbytesread << endl;
+ sftp_close(file);
+ //data(TQByteArray());
+ processedSize((sb->size));
+ }
- return true;
+ sftp_attributes_free(sb);
+ finished();
}
-/** Used to have the server canonicalize any given path name to an absolute path.
-This is useful for converting path names containing ".." components or relative
-pathnames without a leading slash into absolute paths.
-Returns the canonicalized url. */
-int sftpProtocol::sftpRealPath(const KURL& url, KURL& newUrl){
+void sftpProtocol::put(const KURL& url, int permissions, bool overwrite, bool resume) {
+ kdDebug(TDEIO_SFTP_DB) << "put(): " << url.url()
+ << " , permissions = " << TQString::number(permissions)
+ << ", overwrite = " << overwrite
+ << ", resume = " << resume << endl;
- kdDebug(TDEIO_SFTP_DB) << "sftpRealPath(" << url << ", newUrl)" << endl;
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
+ const TQString dest_orig = url.path();
+ const TQByteArray dest_orig_c = dest_orig.utf8();
+ const TQString dest_part = dest_orig + ".part";
+ const TQByteArray dest_part_c = dest_part.utf8();
+ uid_t owner = 0;
+ gid_t group = 0;
+
+ sftp_attributes sb = sftp_lstat(mSftp, dest_orig_c.data());
+ const bool bOrigExists = (sb != NULL);
+ bool bPartExists = false;
+ const bool bMarkPartial = config()->readEntry("MarkPartial", "true") == "true";
+
+ // Don't change permissions of the original file
+ if (bOrigExists) {
+ permissions = sb->permissions;
+ owner = sb->uid;
+ group = sb->gid;
+ }
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
+ if (bMarkPartial) {
+ sftp_attributes sbPart = sftp_lstat(mSftp, dest_part_c.data());
+ bPartExists = (sbPart != NULL);
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << TQ_UINT32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
- s << (TQ_UINT8)SSH2_FXP_REALPATH;
- s << id;
- s.writeBytes(path.data(), len);
+ if (bPartExists && !resume && !overwrite &&
+ sbPart->size > 0 && sbPart->type == SSH_FILEXFER_TYPE_REGULAR) {
+ kdDebug(TDEIO_SFTP_DB) << "put : calling canResume with "
+ << TQString::number(sbPart->size) << endl;
- putPacket(p);
- getPacket(p);
+ // Maybe we can use this partial file for resuming
+ // Tell about the size we have, and the app will tell us
+ // if it's ok to resume or not.
+ if (canResume(sbPart->size)) {
+ resume = true;
+ }
- TQ_UINT8 type;
- TQDataStream r(p, IO_ReadOnly);
+ kdDebug(TDEIO_SFTP_DB) << "put got answer " << resume << endl;
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpRealPath: sftp packet id mismatch" << endl;
- return -1;
+ delete sbPart;
}
+ }
- if( type == SSH2_FXP_STATUS ) {
- TQ_UINT32 code;
- r >> code;
- return code;
+ if (bOrigExists && !(overwrite) && !(resume)) {
+ if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+ error(TDEIO::ERR_DIR_ALREADY_EXIST, dest_orig);
+ } else {
+ error(TDEIO::ERR_FILE_ALREADY_EXIST, dest_orig);
}
+ sftp_attributes_free(sb);
+ return;
+ }
- if( type != SSH2_FXP_NAME ) {
- kdError(TDEIO_SFTP_DB) << "sftpRealPath(): unexpected packet type of " << type << endl;
- return -1;
- }
+ int result;
+ TQByteArray dest;
+ sftp_file file = NULL;
+
+ // Loop until we got 0 (end of data)
+ do {
+ TQByteArray buffer;
+ dataReq(); // Request for data
+ result = readData(buffer);
+
+ if (result >= 0) {
+ if (dest.isEmpty()) {
+ if (bMarkPartial) {
+ kdDebug(TDEIO_SFTP_DB) << "Appending .part extension to " << dest_orig << endl;
+ dest = dest_part_c;
+ if (bPartExists && !(resume)) {
+ kdDebug(TDEIO_SFTP_DB) << "Deleting partial file " << dest_part << endl;
+ sftp_unlink(mSftp, dest_part_c.data());
+ // Catch errors when we try to open the file.
+ }
+ } else {
+ dest = dest_orig_c;
+ if (bOrigExists && !(resume)) {
+ kdDebug(TDEIO_SFTP_DB) << "Deleting destination file " << dest_orig << endl;
+ sftp_unlink(mSftp, dest_orig_c.data());
+ // Catch errors when we try to open the file.
+ }
+ } // bMarkPartial
+
+ if ((resume)) {
+ sftp_attributes fstat;
+
+ kdDebug(TDEIO_SFTP_DB) << "Trying to append: " << dest.data() << endl;
+ file = sftp_open(mSftp, dest.data(), O_RDWR, 0); // append if resuming
+ if (file) {
+ fstat = sftp_fstat(file);
+ if (fstat) {
+ sftp_seek64(file, fstat->size); // Seek to end TODO
+ sftp_attributes_free(fstat);
+ }
+ }
+ } else {
+ mode_t initialMode;
+
+ if (permissions != -1) {
+ initialMode = permissions | S_IWUSR | S_IRUSR;
+ } else {
+ initialMode = 0644;
+ }
+
+ kdDebug(TDEIO_SFTP_DB) << "Trying to open: " << dest.data() << ", mode=" << TQString::number(initialMode) << endl;
+ file = sftp_open(mSftp, dest.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
+ } // resume
+
+ if (file == NULL) {
+ kdDebug(TDEIO_SFTP_DB) << "COULD NOT WRITE " << dest.data()
+ << " permissions=" << permissions
+ << " error=" << ssh_get_error(mSession) << endl;
+ if (sftp_get_error(mSftp) == SSH_FX_PERMISSION_DENIED) {
+ error(TDEIO::ERR_WRITE_ACCESS_DENIED, TQString::fromUtf8(dest));
+ } else {
+ error(TDEIO::ERR_CANNOT_OPEN_FOR_WRITING, TQString::fromUtf8(dest));
+ }
+ sftp_attributes_free(sb);
+ finished();
+ return;
+ } // file
+ } // dest.isEmpty
- TQ_UINT32 count;
- r >> count;
- if( count != 1 ) {
- kdError(TDEIO_SFTP_DB) << "sftpRealPath(): Bad number of file attributes for realpath command" << endl;
- return -1;
+ ssize_t bytesWritten = sftp_write(file, buffer.data(), buffer.size());
+ if (bytesWritten < 0) {
+ error(TDEIO::ERR_COULD_NOT_WRITE, dest_orig);
+ result = -1;
+ }
+ } // result
+ } while (result > 0);
+ sftp_attributes_free(sb);
+
+ // An error occurred deal with it.
+ if (result < 0) {
+ kdDebug(TDEIO_SFTP_DB) << "Error during 'put'. Aborting." << endl;
+
+ if (file != NULL) {
+ sftp_close(file);
+
+ sftp_attributes attr = sftp_stat(mSftp, dest.data());
+ if (bMarkPartial && attr != NULL) {
+ size_t size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE).toLong();
+ if (attr->size < size) {
+ sftp_unlink(mSftp, dest.data());
+ }
+ }
+ delete attr;
+ sftp_attributes_free(attr);
}
- TQCString newPath;
- r >> newPath;
-
- newPath.truncate(newPath.size());
- if (newPath.isEmpty())
- newPath = "/";
- newUrl.setPath(newPath);
+ //::exit(255);
+ finished();
+ return;
+ }
- return SSH2_FX_OK;
-}
+ if (file == NULL) { // we got nothing to write out, so we never opened the file
+ finished();
+ return;
+ }
-sftpProtocol::Status sftpProtocol::doProcessStatus(TQ_UINT8 code, const TQString& message)
-{
- Status res;
- res.code = 0;
- res.size = 0;
- res.text = message;
+ if (sftp_close(file) < 0) {
+ kdWarning(TDEIO_SFTP_DB) << "Error when closing file descriptor" << endl;
+ error(TDEIO::ERR_COULD_NOT_WRITE, dest_orig);
+ return;
+ }
- switch(code)
- {
- case SSH2_FX_OK:
- case SSH2_FX_EOF:
- break;
- case SSH2_FX_NO_SUCH_FILE:
- res.code = ERR_DOES_NOT_EXIST;
- break;
- case SSH2_FX_PERMISSION_DENIED:
- res.code = ERR_ACCESS_DENIED;
- break;
- case SSH2_FX_FAILURE:
- res.text = i18n("SFTP command failed for an unknown reason.");
- res.code = ERR_UNKNOWN;
- break;
- case SSH2_FX_BAD_MESSAGE:
- res.text = i18n("The SFTP server received a bad message.");
- res.code = ERR_UNKNOWN;
- break;
- case SSH2_FX_OP_UNSUPPORTED:
- res.text = i18n("You attempted an operation unsupported by the SFTP server.");
- res.code = ERR_UNKNOWN;
- break;
- default:
- res.text = i18n("Error code: %1").arg(code);
- res.code = ERR_UNKNOWN;
+ // after full download rename the file back to original name
+ if (bMarkPartial) {
+ // If the original URL is a symlink and we were asked to overwrite it,
+ // remove the symlink first. This ensures that we do not overwrite the
+ // current source if the symlink points to it.
+ if ((overwrite)) {
+ sftp_unlink(mSftp, dest_orig_c.data());
}
- return res;
-}
-
-/** Process SSH_FXP_STATUS packets. */
-void sftpProtocol::processStatus(TQ_UINT8 code, const TQString& message){
- Status st = doProcessStatus( code, message );
- if( st.code != 0 )
- error( st.code, st.text );
-}
-
-/** Opens a directory handle for url.path. Returns true if succeeds. */
-int sftpProtocol::sftpOpenDirectory(const KURL& url, TQByteArray& handle){
-
- kdDebug(TDEIO_SFTP_DB) << "sftpOpenDirectory(" << url << ", handle)" << endl;
-
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
- s << (TQ_UINT8)SSH2_FXP_OPENDIR;
- s << (TQ_UINT32)id;
- s.writeBytes(path.data(), len);
-
- putPacket(p);
- getPacket(p);
-
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
-
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpOpenDirectory: sftp packet id mismatch: " <<
- "expected " << expectedId << ", got " << id << endl;
- return -1;
+ if (sftp_rename(mSftp, dest.data(), dest_orig_c.data()) < 0) {
+ kdWarning(TDEIO_SFTP_DB) << " Couldn't rename " << dest.data() << " to " << dest_orig << endl;
+ error(TDEIO::ERR_CANNOT_RENAME_PARTIAL, dest_orig);
+ return;
}
+ }
- if( type == SSH2_FXP_STATUS ) {
- TQ_UINT32 errCode;
- r >> errCode;
- return errCode;
+ // set final permissions
+ if (permissions != -1 && !(resume)) {
+ kdDebug(TDEIO_SFTP_DB) << "Trying to set final permissions of " << dest_orig << " to " << TQString::number(permissions) << endl;
+ if (sftp_chmod(mSftp, dest_orig_c.data(), permissions) < 0) {
+ warning(i18n( "Could not change permissions for\n%1").arg(dest_orig));
}
+ }
- if( type != SSH2_FXP_HANDLE ) {
- kdError(TDEIO_SFTP_DB) << "sftpOpenDirectory: unexpected message type of " << type << endl;
- return -1;
- }
+ // set original owner and group
+ if (bOrigExists) {
+ kdDebug(TDEIO_SFTP_DB) << "Trying to restore original owner and group of " << dest_orig << endl;
+ if (sftp_chown(mSftp, dest_orig_c.data(), owner, group) < 0) {
+ // warning(i18n( "Could not change owner and group for\n%1", dest_orig));
+ }
+ }
- r >> handle;
- if( handle.size() > 256 ) {
- kdError(TDEIO_SFTP_DB) << "sftpOpenDirectory: handle exceeds max length" << endl;
- return -1;
+ // set modification time
+#if 0
+ const TQString mtimeStr = metaData("modified");
+ if (!mtimeStr.isEmpty()) {
+ TQDateTime dt = TQDateTime::fromString(mtimeStr, TQt::ISODate);
+ if (dt.isValid()) {
+ struct timeval times[2];
+
+ sftp_attributes attr = sftp_lstat(mSftp, dest_orig_c.data());
+ if (attr != NULL) {
+ times[0].tv_sec = attr->atime; //// access time, unchanged
+ times[1].tv_sec = dt.toTime_t(); // modification time
+ times[0].tv_usec = times[1].tv_usec = 0;
+
+ sftp_utimes(mSftp, dest_orig_c.data(), times);
+ sftp_attributes_free(attr);
+ }
}
-
- kdDebug(TDEIO_SFTP_DB) << "sftpOpenDirectory: handle (" << handle.size() << "): [" << handle << "]" << endl;
- return SSH2_FX_OK;
+ }
+#endif
+ // We have done our job => finish
+ finished();
}
-/** Closes a directory or file handle. */
-int sftpProtocol::sftpClose(const TQByteArray& handle){
-
- kdDebug(TDEIO_SFTP_DB) << "sftpClose()" << endl;
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
+void sftpProtocol::copy(const KURL &src, const KURL &dest, int permissions, bool overwrite)
+{
+ kdDebug(TDEIO_SFTP_DB) << src.url() << " -> " << dest.url() << " , permissions = " << TQString::number(permissions)
+ << ", overwrite = " << overwrite << endl;
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle.size());
- s << (TQ_UINT8)SSH2_FXP_CLOSE;
- s << (TQ_UINT32)id;
- s << handle;
+ error(TDEIO::ERR_UNSUPPORTED_ACTION, TQString());
+}
- putPacket(p);
- getPacket(p);
+void sftpProtocol::stat(const KURL& url) {
+ kdDebug(TDEIO_SFTP_DB) << url.url() << endl;
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpClose: sftp packet id mismatch" << endl;
- return -1;
- }
+ if (! url.hasPath() || TQDir::isRelativePath(url.path()) ||
+ url.path().contains("/./") || url.path().contains("/../")) {
+ TQString cPath;
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "sftpClose: unexpected message type of " << type << endl;
- return -1;
+ if (url.hasPath()) {
+ cPath = canonicalizePath(url.path());
+ } else {
+ cPath = canonicalizePath(TQString("."));
}
- TQ_UINT32 code;
- r >> code;
- if( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "sftpClose: close failed with err code " << code << endl;
+ if (cPath.isEmpty()) {
+ error(TDEIO::ERR_MALFORMED_URL, url.prettyURL());
+ return;
}
+ KURL redir(url);
+ redir.setPath(cPath);
+ redirection(redir);
- return code;
-}
-
-/** Set a files attributes. */
-int sftpProtocol::sftpSetStat(const KURL& url, const sftpFileAttr& attr){
-
- kdDebug(TDEIO_SFTP_DB) << "sftpSetStat(" << url << ", attr)" << endl;
+ kdDebug(TDEIO_SFTP_DB) << "redirecting to " << redir.url() << endl;
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len + attr.size());
- s << (TQ_UINT8)SSH2_FXP_SETSTAT;
- s << (TQ_UINT32)id;
- s.writeBytes(path.data(), len);
- s << attr;
+ finished();
+ return;
+ }
- putPacket(p);
- getPacket(p);
+ TQByteArray path = url.path().utf8();
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
+ const TQString sDetails = metaData(TQString("details"));
+ const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpSetStat(): sftp packet id mismatch" << endl;
- return -1;
- // XXX How do we do a fatal error?
- }
+ UDSEntry entry;
+ entry.clear();
+ if (!createUDSEntry(url.fileName(), path, entry, details)) {
+ error(TDEIO::ERR_DOES_NOT_EXIST, url.prettyURL());
+ return;
+ }
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "sftpSetStat(): unexpected message type of " << type << endl;
- return -1;
- }
+ statEntry(entry);
- TQ_UINT32 code;
- r >> code;
- if( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "sftpSetStat(): set stat failed with err code " << code << endl;
- }
-
- return code;
+ finished();
}
-/** Sends a sftp command to remove a file or directory. */
-int sftpProtocol::sftpRemove(const KURL& url, bool isfile){
-
- kdDebug(TDEIO_SFTP_DB) << "sftpRemove(): " << url << ", isFile ? " << isfile << endl;
+void sftpProtocol::mimetype(const KURL& url){
+ kdDebug(TDEIO_SFTP_DB) << url.url() << endl;
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
+ // stat() feeds the mimetype
+ statMime(url);
+ closeFile();
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
- s << (TQ_UINT8)(isfile ? SSH2_FXP_REMOVE : SSH2_FXP_RMDIR);
- s << (TQ_UINT32)id;
- s.writeBytes(path.data(), len);
+ finished();
+}
- putPacket(p);
- getPacket(p);
+void sftpProtocol::listDir(const KURL& url) {
+ kdDebug(TDEIO_SFTP_DB) << "list directory: " << url.url() << endl;
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "del(): sftp packet id mismatch" << endl;
- return -1;
- }
+ if (! url.hasPath() || TQDir::isRelativePath(url.path()) ||
+ url.path().contains("/./") || url.path().contains("/../")) {
+ TQString cPath;
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "del(): unexpected message type of " << type << endl;
- return -1;
+ if (url.hasPath()) {
+ cPath = canonicalizePath(url.path());
+ } else {
+ cPath = canonicalizePath(TQString("."));
}
- TQ_UINT32 code;
- r >> code;
- if( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "del(): del failed with err code " << code << endl;
+ if (cPath.isEmpty()) {
+ error(TDEIO::ERR_MALFORMED_URL, url.prettyURL());
+ return;
}
+ KURL redir(url);
+ redir.setPath(cPath);
+ redirection(redir);
- return code;
-}
-
-/** Send a sftp command to rename a file or directoy. */
-int sftpProtocol::sftpRename(const KURL& src, const KURL& dest){
-
- kdDebug(TDEIO_SFTP_DB) << "sftpRename(" << src << " -> " << dest << ")" << endl;
-
- TQCString srcPath = remoteEncoding()->encode(src.path());
- TQCString destPath = remoteEncoding()->encode(dest.path());
+ kdDebug(TDEIO_SFTP_DB) << "redirecting to " << redir.url() << endl;
- uint slen = srcPath.length();
- uint dlen = destPath.length();
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ +
- 4 /*str length*/ + slen +
- 4 /*str length*/ + dlen);
- s << (TQ_UINT8)SSH2_FXP_RENAME;
- s << (TQ_UINT32)id;
- s.writeBytes(srcPath.data(), slen);
- s.writeBytes(destPath.data(), dlen);
-
- putPacket(p);
- getPacket(p);
+ finished();
+ return;
+ }
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
+ TQByteArray path = url.path().utf8();
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpRename(): sftp packet id mismatch" << endl;
- return -1;
- }
+ sftp_dir dp = sftp_opendir(mSftp, path.data());
+ if (dp == NULL) {
+ reportError(url, sftp_get_error(mSftp));
+ return;
+ }
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "sftpRename(): unexpected message type of " << type << endl;
- return -1;
- }
+ sftp_attributes dirent = NULL;
+ const TQString sDetails = metaData(TQString("details"));
+ const int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
+ TQList<TQByteArray> entryNames;
+ UDSEntry entry;
- int code;
- r >> code;
- if( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "sftpRename(): rename failed with err code " << code << endl;
- }
+ kdDebug(TDEIO_SFTP_DB) << "readdir: " << path.data() << ", details: " << TQString::number(details) << endl;
- return code;
-}
-/** Get directory listings. */
-int sftpProtocol::sftpReadDir(const TQByteArray& handle, const KURL& url){
- // url is needed so we can lookup the link destination
- kdDebug(TDEIO_SFTP_DB) << "sftpReadDir(): " << url << endl;
-
- TQ_UINT32 id, expectedId, count;
- TQ_UINT8 type;
-
- sftpFileAttr attr (remoteEncoding());
- attr.setDirAttrsFlag(true);
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- id = expectedId = mMsgId++;
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle.size());
- s << (TQ_UINT8)SSH2_FXP_READDIR;
- s << (TQ_UINT32)id;
- s << handle;
-
- putPacket(p);
- getPacket(p);
-
- TQDataStream r(p, IO_ReadOnly);
- r >> type >> id;
-
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpReadDir(): sftp packet id mismatch" << endl;
- return -1;
- }
+ UDSAtom atom;
- int code;
- if( type == SSH2_FXP_STATUS ) {
- r >> code;
- return code;
- }
+ for (;;) {
+ mode_t access;
+ mode_t type;
+ char *link;
- if( type != SSH2_FXP_NAME ) {
- kdError(TDEIO_SFTP_DB) << "tdeio_sftpProtocl::sftpReadDir(): Unexpected message" << endl;
- return -1;
+ dirent = sftp_readdir(mSftp, dp);
+ if (dirent == NULL) {
+ break;
}
- r >> count;
- kdDebug(TDEIO_SFTP_DB) << "sftpReadDir(): got " << count << " entries" << endl;
-
- while(count--) {
- r >> attr;
+ entry.clear();
+ atom.m_uds = UDS_NAME;
+ atom.m_str = TQFile::decodeName(dirent->name);
+ entry.append(atom);
- if( S_ISLNK(attr.permissions()) ) {
- KURL myurl ( url );
- myurl.addPath(attr.filename());
+ if (dirent->type == SSH_FILEXFER_TYPE_SYMLINK) {
+ TQCString file = (TQString(path) + "/" + TQFile::decodeName(dirent->name)).utf8().data();
- // Stat the symlink to find out its type...
- sftpFileAttr attr2 (remoteEncoding());
- (void) sftpStat(myurl, attr2);
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
- attr.setLinkType(attr2.linkType());
- attr.setLinkDestination(attr2.linkDestination());
+ link = sftp_readlink(mSftp, file.data());
+ if (link == NULL) {
+ sftp_attributes_free(dirent);
+ error(TDEIO::ERR_INTERNAL, i18n("Could not read link: %1").arg(TQString::fromUtf8(file)));
+ return;
+ }
+ atom.m_uds = UDS_LINK_DEST;
+ atom.m_str = TQFile::decodeName(link);
+ entry.append(atom);
+ delete link;
+ // A symlink -> follow it only if details > 1
+ if (details > 1) {
+ sftp_attributes sb = sftp_stat(mSftp, file.data());
+ if (sb == NULL) {
+ // It is a link pointing to nowhere
+ type = S_IFMT - 1;
+ access = S_IRWXU | S_IRWXG | S_IRWXO;
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = type;
+ entry.append(atom);
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = access;
+ entry.append(atom);
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = 0;
+ entry.append(atom);
+
+ goto notype;
}
-
- listEntry(attr.entry(), false);
+ sftp_attributes_free(dirent);
+ dirent = sb;
+ }
}
- listEntry(attr.entry(), true);
-
- return SSH2_FX_OK;
-}
-
-int sftpProtocol::sftpReadLink(const KURL& url, TQString& target){
-
- kdDebug(TDEIO_SFTP_DB) << "sftpReadLink(): " << url << endl;
-
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
-
- //kdDebug(TDEIO_SFTP_DB) << "sftpReadLink(): Encoded Path: " << path << endl;
- //kdDebug(TDEIO_SFTP_DB) << "sftpReadLink(): Encoded Size: " << len << endl;
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
- s << (TQ_UINT8)SSH2_FXP_READLINK;
- s << id;
- s.writeBytes(path.data(), len);
-
-
- putPacket(p);
- getPacket(p);
-
- TQ_UINT8 type;
- TQDataStream r(p, IO_ReadOnly);
+ switch (dirent->type) {
+ case SSH_FILEXFER_TYPE_REGULAR:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFREG;
+ entry.append(atom);
+ break;
+ case SSH_FILEXFER_TYPE_DIRECTORY:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFDIR;
+ entry.append(atom);
+ break;
+ case SSH_FILEXFER_TYPE_SYMLINK:
+ atom.m_uds = UDS_FILE_TYPE;
+ atom.m_long = S_IFLNK;
+ entry.append(atom);
+ break;
+ case SSH_FILEXFER_TYPE_SPECIAL:
+ case SSH_FILEXFER_TYPE_UNKNOWN:
+ break;
+ }
+
+ access = dirent->permissions & 07777;
+ atom.m_uds = UDS_ACCESS;
+ atom.m_long = access;
+ entry.append(atom);
+
+ atom.m_uds = UDS_SIZE;
+ atom.m_long = dirent->size;
+ entry.append(atom);
+
+notype:
+ if (details > 0) {
+ atom.m_uds = UDS_USER;
+ if (dirent->owner) {
+ atom.m_str = TQString::fromUtf8(dirent->owner);
+ } else {
+ atom.m_str = TQString::number(dirent->uid);
+ }
+ entry.append(atom);
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpReadLink(): sftp packet id mismatch" << endl;
- return -1;
- }
+ atom.m_uds = UDS_GROUP;
+ if (dirent->group) {
+ atom.m_str = TQString::fromUtf8(dirent->group);
+ } else {
+ atom.m_str = TQString::number(dirent->gid);
+ }
+ entry.append(atom);
- if( type == SSH2_FXP_STATUS ) {
- TQ_UINT32 code;
- r >> code;
- kdDebug(TDEIO_SFTP_DB) << "sftpReadLink(): read link failed with code " << code << endl;
- return code;
- }
+ atom.m_uds = UDS_ACCESS_TIME;
+ atom.m_long = dirent->atime;
+ entry.append(atom);
- if( type != SSH2_FXP_NAME ) {
- kdError(TDEIO_SFTP_DB) << "sftpReadLink(): unexpected packet type of " << type << endl;
- return -1;
- }
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ atom.m_long = dirent->mtime;
+ entry.append(atom);
- TQ_UINT32 count;
- r >> count;
- if( count != 1 ) {
- kdError(TDEIO_SFTP_DB) << "sftpReadLink(): Bad number of file attributes for realpath command" << endl;
- return -1;
+ atom.m_uds = UDS_MODIFICATION_TIME;
+ atom.m_long = dirent->createtime;
+ entry.append(atom);
}
- TQCString linkAddress;
- r >> linkAddress;
-
- linkAddress.truncate(linkAddress.size());
- kdDebug(TDEIO_SFTP_DB) << "sftpReadLink(): Link address: " << linkAddress << endl;
+ sftp_attributes_free(dirent);
+ listEntry(entry, false);
+ } // for ever
+ sftp_closedir(dp);
+ listEntry(entry, true); // ready
- target = remoteEncoding()->decode(linkAddress);
-
- return SSH2_FX_OK;
+ finished();
}
-int sftpProtocol::sftpSymLink(const TQString& _target, const KURL& dest){
-
- TQCString destPath = remoteEncoding()->encode(dest.path());
- TQCString target = remoteEncoding()->encode(_target);
- uint dlen = destPath.length();
- uint tlen = target.length();
-
- kdDebug(TDEIO_SFTP_DB) << "sftpSymLink(" << target << " -> " << destPath << ")" << endl;
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
+void sftpProtocol::mkdir(const KURL &url, int permissions) {
+ kdDebug(TDEIO_SFTP_DB) << "create directory: " << url.url() << endl;
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ +
- 4 /*str length*/ + tlen +
- 4 /*str length*/ + dlen);
- s << (TQ_UINT8)SSH2_FXP_SYMLINK;
- s << (TQ_UINT32)id;
- s.writeBytes(target.data(), tlen);
- s.writeBytes(destPath.data(), dlen);
-
- putPacket(p);
- getPacket(p);
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
+ if (url.path().isEmpty()) {
+ error(TDEIO::ERR_MALFORMED_URL, url.prettyURL());
+ return;
+ }
+ const TQString path = url.path();
+ const TQByteArray path_c = path.utf8();
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpSymLink(): sftp packet id mismatch" << endl;
- return -1;
- }
+ // Remove existing file or symlink, if requested.
+ if (metaData(TQString("overwrite")) == TQString("true")) {
+ kdDebug(TDEIO_SFTP_DB) << "overwrite set, remove existing file or symlink: " << url.url() << endl;
+ sftp_unlink(mSftp, path_c.data());
+ }
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "sftpSymLink(): unexpected message type of " << type << endl;
- return -1;
+ kdDebug(TDEIO_SFTP_DB) << "Trying to create directory: " << path << endl;
+ sftp_attributes sb = sftp_lstat(mSftp, path_c.data());
+ if (sb == NULL) {
+ if (sftp_mkdir(mSftp, path_c.data(), 0777) < 0) {
+ reportError(url, sftp_get_error(mSftp));
+ sftp_attributes_free(sb);
+ return;
+ } else {
+ kdDebug(TDEIO_SFTP_DB) << "Successfully created directory: " << url.url() << endl;
+ if (permissions != -1) {
+ chmod(url, permissions);
+ } else {
+ finished();
+ }
+ sftp_attributes_free(sb);
+ return;
}
+ }
- TQ_UINT32 code;
- r >> code;
- if( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "sftpSymLink(): rename failed with err code " << code << endl;
- }
+ if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+ error(TDEIO::ERR_DIR_ALREADY_EXIST, path);
+ } else {
+ error(TDEIO::ERR_FILE_ALREADY_EXIST, path);
+ }
- return code;
+ sftp_attributes_free(sb);
+ return;
}
-/** Stats a file. */
-int sftpProtocol::sftpStat(const KURL& url, sftpFileAttr& attr) {
-
- kdDebug(TDEIO_SFTP_DB) << "sftpStat(): " << url << endl;
-
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
- s << (TQ_UINT8)SSH2_FXP_LSTAT;
- s << (TQ_UINT32)id;
- s.writeBytes(path.data(), len);
-
- putPacket(p);
- getPacket(p);
+void sftpProtocol::rename(const KURL& src, const KURL& dest, bool overwrite) {
+ kdDebug(TDEIO_SFTP_DB) << "rename " << src.url() << " to " << dest.url() << endl;
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
-
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpStat(): sftp packet id mismatch" << endl;
- return -1;
- }
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- if( type == SSH2_FXP_STATUS ) {
- TQ_UINT32 errCode;
- r >> errCode;
- kdError(TDEIO_SFTP_DB) << "sftpStat(): stat failed with code " << errCode << endl;
- return errCode;
- }
+ TQByteArray qsrc = src.path().utf8();
+ TQByteArray qdest = dest.path().utf8();
- if( type != SSH2_FXP_ATTRS ) {
- kdError(TDEIO_SFTP_DB) << "sftpStat(): unexpected message type of " << type << endl;
- return -1;
+ sftp_attributes sb = sftp_lstat(mSftp, qdest.data());
+ if (sb != NULL) {
+ if (!overwrite) {
+ if (sb->type == SSH_FILEXFER_TYPE_DIRECTORY) {
+ error(TDEIO::ERR_DIR_ALREADY_EXIST, dest.url());
+ } else {
+ error(TDEIO::ERR_FILE_ALREADY_EXIST, dest.url());
+ }
+ sftp_attributes_free(sb);
+ return;
}
- r >> attr;
- attr.setFilename(url.fileName());
- kdDebug(TDEIO_SFTP_DB) << "sftpStat(): " << attr << endl;
-
- // If the stat'ed resource is a symlink, perform a recursive stat
- // to determine the actual destination's type (file/dir).
- if( S_ISLNK(attr.permissions()) && isSupportedOperation(SSH2_FXP_READLINK) ) {
-
- TQString target;
- int code = sftpReadLink( url, target );
-
- if ( code != SSH2_FX_OK ) {
- kdError(TDEIO_SFTP_DB) << "sftpStat(): Unable to stat symlink destination" << endl;
- return -1;
- }
-
- kdDebug(TDEIO_SFTP_DB) << "sftpStat(): Resource is a symlink to -> " << target << endl;
-
- KURL dest( url );
- if( target[0] == '/' )
- dest.setPath(target);
- else
- dest.setFileName(target);
-
- dest.cleanPath();
-
- // Ignore symlinks that point to themselves...
- if ( dest != url ) {
-
- sftpFileAttr attr2 (remoteEncoding());
- (void) sftpStat(dest, attr2);
-
- if (attr2.linkType() == 0)
- attr.setLinkType(attr2.fileType());
- else
- attr.setLinkType(attr2.linkType());
-
- attr.setLinkDestination(target);
+ del(dest, sb->type == SSH_FILEXFER_TYPE_DIRECTORY ? true : false);
+ }
+ sftp_attributes_free(sb);
- kdDebug(TDEIO_SFTP_DB) << "sftpStat(): File type: " << attr.fileType() << endl;
- }
- }
+ if (sftp_rename(mSftp, qsrc.data(), qdest.data()) < 0) {
+ reportError(dest, sftp_get_error(mSftp));
+ return;
+ }
- return SSH2_FX_OK;
+ finished();
}
+void sftpProtocol::symlink(const TQString& target, const KURL& dest, bool overwrite) {
+ kdDebug(TDEIO_SFTP_DB) << "link " << target << "->" << dest.url()
+ << ", overwrite = " << overwrite << endl;
-int sftpProtocol::sftpOpen(const KURL& url, const TQ_UINT32 pflags,
- const sftpFileAttr& attr, TQByteArray& handle) {
- kdDebug(TDEIO_SFTP_DB) << "sftpOpen(" << url << ", handle" << endl;
-
- TQCString path = remoteEncoding()->encode(url.path());
- uint len = path.length();
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
-
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ +
- 4 /*str length*/ + len +
- 4 /*pflags*/ + attr.size());
- s << (TQ_UINT8)SSH2_FXP_OPEN;
- s << (TQ_UINT32)id;
- s.writeBytes(path.data(), len);
- s << pflags;
- s << attr;
-
- putPacket(p);
- getPacket(p);
-
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
-
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpOpen(): sftp packet id mismatch" << endl;
- return -1;
- }
-
- if( type == SSH2_FXP_STATUS ) {
- TQ_UINT32 errCode;
- r >> errCode;
- return errCode;
- }
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- if( type != SSH2_FXP_HANDLE ) {
- kdError(TDEIO_SFTP_DB) << "sftpOpen(): unexpected message type of " << type << endl;
- return -1;
+ TQByteArray t = target.utf8();
+ TQByteArray d = dest.path().utf8();
+
+ bool failed = false;
+ if (sftp_symlink(mSftp, t.data(), d.data()) < 0) {
+ if (overwrite) {
+ sftp_attributes sb = sftp_lstat(mSftp, d.data());
+ if (sb == NULL) {
+ failed = true;
+ } else {
+ if (sftp_unlink(mSftp, d.data()) < 0) {
+ failed = true;
+ } else {
+ if (sftp_symlink(mSftp, t.data(), d.data()) < 0) {
+ failed = true;
+ }
+ }
+ }
+ sftp_attributes_free(sb);
}
+ }
- r >> handle;
- if( handle.size() > 256 ) {
- kdError(TDEIO_SFTP_DB) << "sftpOpen(): handle exceeds max length" << endl;
- return -1;
- }
+ if (failed) {
+ reportError(dest, sftp_get_error(mSftp));
+ return;
+ }
- kdDebug(TDEIO_SFTP_DB) << "sftpOpen(): handle (" << handle.size() << "): [" << handle << "]" << endl;
- return SSH2_FX_OK;
+ finished();
}
+void sftpProtocol::chmod(const KURL& url, int permissions) {
+ kdDebug(TDEIO_SFTP_DB) << "change permission of " << url.url() << " to " << TQString::number(permissions) << endl;
-int sftpProtocol::sftpRead(const TQByteArray& handle, TDEIO::filesize_t offset, TQ_UINT32 len, TQByteArray& data)
-{
- // kdDebug(TDEIO_SFTP_DB) << "sftpRead( offset = " << offset << ", len = " << len << ")" << endl;
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ +
- 4 /*str length*/ + handle.size() +
- 8 /*offset*/ + 4 /*length*/);
- s << (TQ_UINT8)SSH2_FXP_READ;
- s << (TQ_UINT32)id;
- s << handle;
- s << offset; // we don't have a convienient 64 bit int so set upper int to zero
- s << len;
-
- putPacket(p);
- getPacket(p);
-
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
-
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpRead: sftp packet id mismatch" << endl;
- return -1;
- }
-
- if( type == SSH2_FXP_STATUS ) {
- TQ_UINT32 errCode;
- r >> errCode;
- kdError(TDEIO_SFTP_DB) << "sftpRead: read failed with code " << errCode << endl;
- return errCode;
- }
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- if( type != SSH2_FXP_DATA ) {
- kdError(TDEIO_SFTP_DB) << "sftpRead: unexpected message type of " << type << endl;
- return -1;
- }
+ TQByteArray path = url.path().utf8();
- r >> data;
+ if (sftp_chmod(mSftp, path.data(), permissions) < 0) {
+ reportError(url, sftp_get_error(mSftp));
+ return;
+ }
- return SSH2_FX_OK;
+ finished();
}
+void sftpProtocol::del(const KURL &url, bool isfile){
+ kdDebug(TDEIO_SFTP_DB) << "deleting " << (isfile ? "file: " : "directory: ") << url.url() << endl;
-int sftpProtocol::sftpWrite(const TQByteArray& handle, TDEIO::filesize_t offset, const TQByteArray& data){
-// kdDebug(TDEIO_SFTP_DB) << "sftpWrite( offset = " << offset <<
-// ", data sz = " << data.size() << ")" << endl;
- TQByteArray p;
- TQDataStream s(p, IO_WriteOnly);
-
- TQ_UINT32 id, expectedId;
- id = expectedId = mMsgId++;
- s << (TQ_UINT32)(1 /*type*/ + 4 /*id*/ +
- 4 /*str length*/ + handle.size() +
- 8 /*offset*/ +
- 4 /* data size */ + data.size());
- s << (TQ_UINT8)SSH2_FXP_WRITE;
- s << (TQ_UINT32)id;
- s << handle;
- s << offset; // we don't have a convienient 64 bit int so set upper int to zero
- s << data;
-
-// kdDebug(TDEIO_SFTP_DB) << "sftpWrite(): SSH2_FXP_WRITE, id:"
-// << id << ", handle:" << handle << ", offset:" << offset << ", some data" << endl;
-
-// kdDebug(TDEIO_SFTP_DB) << "sftpWrite(): send packet [" << p << "]" << endl;
-
- putPacket(p);
- getPacket(p);
-
-// kdDebug(TDEIO_SFTP_DB) << "sftpWrite(): received packet [" << p << "]" << endl;
+ openConnection();
+ if (!mConnected) {
+ return;
+ }
- TQDataStream r(p, IO_ReadOnly);
- TQ_UINT8 type;
+ TQByteArray path = url.path().utf8();
- r >> type >> id;
- if( id != expectedId ) {
- kdError(TDEIO_SFTP_DB) << "sftpWrite(): sftp packet id mismatch, got "
- << id << ", expected " << expectedId << endl;
- return -1;
+ if (isfile) {
+ if (sftp_unlink(mSftp, path.data()) < 0) {
+ reportError(url, sftp_get_error(mSftp));
+ return;
}
-
- if( type != SSH2_FXP_STATUS ) {
- kdError(TDEIO_SFTP_DB) << "sftpWrite(): unexpected message type of " << type << endl;
- return -1;
+ } else {
+ if (sftp_rmdir(mSftp, path.data()) < 0) {
+ reportError(url, sftp_get_error(mSftp));
+ return;
}
+ }
- TQ_UINT32 code;
- r >> code;
- return code;
+ finished();
}
+void sftpProtocol::slave_status() {
+ kdDebug(TDEIO_SFTP_DB) << "connected to " << mHost << "?: " << mConnected << endl;
+ slaveStatus((mConnected ? mHost : TQString()), mConnected);
+}
diff --git a/tdeioslave/sftp/tdeio_sftp.h b/tdeioslave/sftp/tdeio_sftp.h
index 30c452f9b..88ddaecf1 100644
--- a/tdeioslave/sftp/tdeio_sftp.h
+++ b/tdeioslave/sftp/tdeio_sftp.h
@@ -1,37 +1,49 @@
-/***************************************************************************
- sftpProtocol.h - description
- -------------------
- begin : Sat Jun 30 20:08:47 CDT 2001
- copyright : (C) 2001 by Lucas Fisher
- email : ljfisher@purdue.edu
-***************************************************************************/
-
-/***************************************************************************
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- ***************************************************************************/
+/*
+ * Copyright (c) 2001 Lucas Fisher <ljfisher@purdue.edu>
+ * Copyright (c) 2009 Andreas Schneider <mail@cynapses.org>
+ * Copyright (c) 2020 Martin Sandsmark <martin@sandsmark.ninja>
+ * KDE2 port
+ * Copyright (c) 2022 Mavridis Philippe <mavridisf@gmail.com>
+ * Trinity port
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License (LGPL) as published by the Free Software Foundation;
+ * either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
#ifndef __tdeio_sftp_h__
#define __tdeio_sftp_h__
-#include <tqstring.h>
-#include <tqcstring.h>
-#include <tqobject.h>
-
#include <kurl.h>
#include <tdeio/global.h>
#include <tdeio/slavebase.h>
#include <kdebug.h>
+#include <stdint.h>
-#include "process.h"
-#include "sftpfileattr.h"
-#include "ksshprocess.h"
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+#include <libssh/callbacks.h>
+// How big should each data packet be? Definitely not bigger than 64kb or
+// you will overflow the 2 byte size variable in a sftp packet.
+#define MAX_XFER_BUF_SIZE 60 * 1024
#define TDEIO_SFTP_DB 7120
+namespace TDEIO {
+ class AuthInfo;
+}
class sftpProtocol : public TDEIO::SlaveBase
{
@@ -39,23 +51,35 @@ class sftpProtocol : public TDEIO::SlaveBase
public:
sftpProtocol(const TQCString &pool_socket, const TQCString &app_socket);
virtual ~sftpProtocol();
- virtual void setHost(const TQString& h, int port, const TQString& user, const TQString& pass);
- virtual void get(const KURL& url);
- virtual void listDir(const KURL& url) ;
- virtual void mimetype(const KURL& url);
- virtual void stat(const KURL& url);
- virtual void copy(const KURL &src, const KURL &dest, int permissions, bool overwrite);
- virtual void put(const KURL& url, int permissions, bool overwrite, bool resume);
- virtual void closeConnection();
- virtual void slave_status();
- virtual void del(const KURL &url, bool isfile);
- virtual void chmod(const KURL& url, int permissions);
- virtual void symlink(const TQString& target, const KURL& dest, bool overwrite);
- virtual void rename(const KURL& src, const KURL& dest, bool overwrite);
- virtual void mkdir(const KURL&url, int permissions);
- virtual void openConnection();
+ virtual void setHost(const TQString& h, int port, const TQString& user, const TQString& pass) override;
+ virtual void get(const KURL& url) override;
+ virtual void listDir(const KURL& url) override;
+ virtual void mimetype(const KURL& url) override;
+ virtual void stat(const KURL& url) override;
+ virtual void put(const KURL& url, int permissions, bool overwrite, bool resume) override;
+ virtual void copy(const KURL &src, const KURL &dest, int permissions, bool overwrite) override;
+ virtual void closeConnection() override;
+ virtual void slave_status() override;
+ virtual void del(const KURL &url, bool isfile) override;
+ virtual void chmod(const KURL& url, int permissions) override;
+ virtual void symlink(const TQString& target, const KURL& dest, bool overwrite) override;
+ virtual void rename(const KURL& src, const KURL& dest, bool overwrite) override;
+ virtual void mkdir(const KURL& url, int permissions) override;
+ virtual void openConnection() override;
+
+ // libssh authentication callback (note that this is called by the
+ // global ::auth_callback() call.
+ int auth_callback(const char *prompt, char *buf, size_t len,
+ int echo, int verify, void *userdata);
+
+ // libssh logging callback (note that this is called by the
+ // global ::log_callback() call.
+ void log_callback(ssh_session session, int priority, const char *message,
+ void *userdata);
private: // Private variables
+ void statMime(const KURL &url);
+ void closeFile();
/** True if ioslave is connected to sftp server. */
bool mConnected;
@@ -65,8 +89,11 @@ private: // Private variables
/** Port we are connected to. */
int mPort;
- /** Ssh process to which we send the sftp packets. */
- KSshProcess ssh;
+ /** The ssh session for the connection */
+ ssh_session mSession;
+
+ /** The sftp session for the connection */
+ sftp_session mSftp;
/** Username to use when connecting */
TQString mUsername;
@@ -74,76 +101,34 @@ private: // Private variables
/** User's password */
TQString mPassword;
- /** Message id of the last sftp packet we sent. */
- unsigned int mMsgId;
+ /** The open file */
+ sftp_file mOpenFile;
- /** Type of packet we are expecting to receive next. */
- unsigned char mExpected;
+ /** The open URL */
+ KURL mOpenUrl;
+
+ ssh_callbacks mCallbacks;
/** Version of the sftp protocol we are using. */
int sftpVersion;
-
- struct Status
- {
- int code;
- TDEIO::filesize_t size;
- TQString text;
- };
+
+ //struct Status
+ //{
+ // int code;
+ // TDEIO::filesize_t size;
+ // TQString text;
+ //};
private: // private methods
- bool getPacket(TQByteArray& msg);
-
- /* Type is a sftp packet type found in .sftp.h'.
- * Example: SSH2_FXP_READLINK, SSH2_FXP_RENAME, etc.
- *
- * Returns true if the type is supported by the sftp protocol
- * version negotiated by the client and server (sftpVersion).
- */
- bool isSupportedOperation(int type);
- /** Used to have the server canonicalize any given path name to an absolute path.
- This is useful for converting path names containing ".." components or relative
- pathnames without a leading slash into absolute paths.
- Returns the canonicalized url. */
- int sftpRealPath(const KURL& url, KURL& newUrl);
-
- /** Send an sftp packet to stdin of the ssh process. */
- bool putPacket(TQByteArray& p);
- /** Process SSH_FXP_STATUS packets. */
- void processStatus(TQ_UINT8, const TQString& message = TQString::null);
- /** Process SSH_FXP_STATUS packes and return the result. */
- Status doProcessStatus(TQ_UINT8, const TQString& message = TQString::null);
- /** Opens a directory handle for url.path. Returns true if succeeds. */
- int sftpOpenDirectory(const KURL& url, TQByteArray& handle);
- /** Closes a directory or file handle. */
- int sftpClose(const TQByteArray& handle);
- /** Send a sftp command to rename a file or directoy. */
- int sftpRename(const KURL& src, const KURL& dest);
- /** Set a files attributes. */
- int sftpSetStat(const KURL& url, const sftpFileAttr& attr);
- /** Sends a sftp command to remove a file or directory. */
- int sftpRemove(const KURL& url, bool isfile);
- /** Creates a symlink named dest to target. */
- int sftpSymLink(const TQString& target, const KURL& dest);
- /** Get directory listings. */
- int sftpReadDir(const TQByteArray& handle, const KURL& url);
- /** Retrieves the destination of a link. */
- int sftpReadLink(const KURL& url, TQString& target);
- /** Stats a file. */
- int sftpStat(const KURL& url, sftpFileAttr& attr);
- /** No descriptions */
- int sftpOpen(const KURL& url, const TQ_UINT32 pflags, const sftpFileAttr& attr, TQByteArray& handle);
- /** No descriptions */
- int sftpRead(const TQByteArray& handle, TDEIO::filesize_t offset, TQ_UINT32 len, TQByteArray& data);
- /** No descriptions */
- int sftpWrite(const TQByteArray& handle, TDEIO::filesize_t offset, const TQByteArray& data);
-
- /** Performs faster upload when the source is a local file... */
- void sftpCopyPut(const KURL& src, const KURL& dest, int mode, bool overwrite);
- /** Performs faster download when the destination is a local file... */
- void sftpCopyGet(const KURL& dest, const KURL& src, int mode, bool overwrite);
-
- /** */
- Status sftpGet( const KURL& src, TDEIO::filesize_t offset = 0, int fd = -1);
- void sftpPut( const KURL& dest, int permissions, bool resume, bool overwrite, int fd = -1);
+
+ int authenticateKeyboardInteractive(TDEIO::AuthInfo &info);
+
+ void reportError(const KURL &url, const int err);
+
+ bool createUDSEntry(const TQString &filename, const TQByteArray &path,
+ TDEIO::UDSEntry &entry, short int details);
+
+ TQString canonicalizePath(const TQString &path);
};
+
#endif