1 /***************************************************************************
4 begin : Fri Jun 29 23:45:40 CDT 2001
5 copyright : (C) 2001 by Lucas Fisher
6 email : ljfisher@purdue.edu
7 ***************************************************************************/
9 /***************************************************************************
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
16 ***************************************************************************/
20 We are pretty much left with kDebug messages for debugging. We can't use a gdb
21 as described in the ioslave DEBUG.howto because kdeinit has to run in a terminal.
22 Ssh will detect this terminal and ask for a password there, but will just get garbage.
28 #include <config-runtime.h>
32 #include <QtCore/QBuffer>
33 #include <QtCore/QByteArray>
34 #include <QtCore/QFile>
35 #include <QtCore/QObject>
36 #include <QtCore/QString>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
51 #include <sys/types.h>
54 #include <kapplication.h>
57 #include <kmessagebox.h>
58 #include <kcomponentdata.h>
60 #include <kstandarddirs.h>
63 #include <kio/ioslave_defaults.h>
64 #include <kmimetype.h>
66 #include <kremoteencoding.h>
67 #include <kconfiggroup.h>
71 #include "sftpfileattr.h"
72 #include "ksshprocess.h"
78 int KDE_EXPORT
kdemain( int argc
, char **argv
)
80 KComponentData
componentData( "kio_sftp" );
82 kDebug(KIO_SFTP_DB
) << "*** Starting kio_sftp ";
85 kDebug(KIO_SFTP_DB
) << "Usage: kio_sftp protocol domain-socket1 domain-socket2";
89 sftpProtocol
slave(argv
[2], argv
[3]);
92 kDebug(KIO_SFTP_DB
) << "*** kio_sftp Done";
99 * This helper handles some special issues (blocking and interrupted
100 * system call) when writing to a file handle.
102 * @return 0 on success or an error code on failure (ERR_COULD_NOT_WRITE,
103 * ERR_DISK_FULL, ERR_CONNECTION_BROKEN).
105 static int writeToFile (int fd
, const char *buf
, size_t len
)
109 ssize_t written
= ::write(fd
, buf
, len
);
122 return ERR_CONNECTION_BROKEN
;
124 return ERR_DISK_FULL
;
126 return ERR_COULD_NOT_WRITE
;
132 sftpProtocol::sftpProtocol(const QByteArray
&pool_socket
, const QByteArray
&app_socket
)
133 : SlaveBase("kio_sftp", pool_socket
, app_socket
),
134 mConnected(false), mPort(-1), mMsgId(0) {
136 kDebug(KIO_SFTP_DB
) << "sftpProtocol(): pid = " << getpid();
141 sftpProtocol::~sftpProtocol() {
143 kDebug(KIO_SFTP_DB
) << "~sftpProtocol(): pid = " << getpid();
149 * Type is a sftp packet type found in .sftp.h'.
150 * Example: SSH2_FXP_READLINK, SSH2_FXP_RENAME, etc.
152 * Returns true if the type is supported by the sftp protocol
153 * version negotiated by the client and server (sftpVersion).
155 bool sftpProtocol::isSupportedOperation(int type
) {
157 case SSH2_FXP_VERSION
:
158 case SSH2_FXP_STATUS
:
159 case SSH2_FXP_HANDLE
:
170 case SSH2_FXP_SETSTAT
:
171 case SSH2_FXP_FSETSTAT
:
172 case SSH2_FXP_OPENDIR
:
173 case SSH2_FXP_READDIR
:
174 case SSH2_FXP_REMOVE
:
177 case SSH2_FXP_REALPATH
:
180 case SSH2_FXP_RENAME
:
181 return sftpVersion
>= 2 ? true : false;
182 case SSH2_FXP_EXTENDED
:
183 case SSH2_FXP_EXTENDED_REPLY
:
184 case SSH2_FXP_READLINK
:
185 case SSH2_FXP_SYMLINK
:
186 return sftpVersion
>= 3 ? true : false;
188 kDebug(KIO_SFTP_DB
) << "isSupportedOperation(type:"
189 << type
<< "): unrecognized operation type" << endl
;
196 void sftpProtocol::copy(const KUrl
&src
, const KUrl
&dest
, int permissions
, KIO::JobFlags flags
)
198 kDebug(KIO_SFTP_DB
) << "copy(): " << src
<< " -> " << dest
;
200 bool srcLocal
= src
.isLocalFile();
201 bool destLocal
= dest
.isLocalFile();
203 if ( srcLocal
&& !destLocal
) // Copy file -> sftp
204 sftpCopyPut(src
, dest
, permissions
, flags
);
205 else if ( destLocal
&& !srcLocal
) // Copy sftp -> file
206 sftpCopyGet(dest
, src
, permissions
, flags
);
208 error(ERR_UNSUPPORTED_ACTION
, QString());
211 void sftpProtocol::sftpCopyGet(const KUrl
& dest
, const KUrl
& src
, int mode
, KIO::JobFlags flags
)
213 kDebug(KIO_SFTP_DB
) << "sftpCopyGet(): " << src
<< " -> " << dest
;
215 // Attempt to establish a connection...
220 KDE_struct_stat buff_orig
;
221 QByteArray
dest_orig ( QFile::encodeName(dest
.path()) );
222 bool origExists
= (KDE_lstat( dest_orig
.data(), &buff_orig
) != -1);
226 if (S_ISDIR(buff_orig
.st_mode
))
228 error(ERR_IS_DIRECTORY
, dest
.prettyUrl());
232 if (!(flags
& KIO::Overwrite
))
234 error(ERR_FILE_ALREADY_EXIST
, dest
.prettyUrl());
239 KIO::filesize_t offset
= 0;
240 QByteArray
dest_part ( dest_orig
+ ".part" );
243 bool partExists
= false;
244 bool markPartial
= config()->readEntry("MarkPartial", true);
248 KDE_struct_stat buff_part
;
249 partExists
= (KDE_stat( dest_part
.data(), &buff_part
) != -1);
251 if (partExists
&& buff_part
.st_size
> 0 && S_ISREG(buff_part
.st_mode
))
253 if (canResume( buff_part
.st_size
))
255 offset
= buff_part
.st_size
;
256 kDebug(KIO_SFTP_DB
) << "sftpCopyGet: Resuming @ " << offset
;
262 fd
= KDE_open(dest_part
.data(), O_RDWR
);
263 offset
= KDE_lseek(fd
, 0, SEEK_END
);
266 error(ERR_CANNOT_RESUME
, dest
.prettyUrl());
272 // Set up permissions properly, based on what is done in file io-slave
273 int openFlags
= (O_CREAT
| O_TRUNC
| O_WRONLY
);
274 int initialMode
= (mode
== -1) ? 0666 : (mode
| S_IWUSR
);
275 fd
= KDE_open(dest_part
.data(), openFlags
, initialMode
);
280 // Set up permissions properly, based on what is done in file io-slave
281 int openFlags
= (O_CREAT
| O_TRUNC
| O_WRONLY
);
282 int initialMode
= (mode
== -1) ? 0666 : (mode
| S_IWUSR
);
283 fd
= KDE_open(dest_orig
.data(), openFlags
, initialMode
);
288 kDebug(KIO_SFTP_DB
) << "sftpCopyGet: Unable to open (" << fd
<< ") for writing.";
290 error (ERR_WRITE_ACCESS_DENIED
, dest
.prettyUrl());
292 error (ERR_CANNOT_OPEN_FOR_WRITING
, dest
.prettyUrl());
296 Status info
= sftpGet(src
, offset
, fd
);
297 if ( info
.code
!= 0 )
299 // Should we keep the partially downloaded file ??
300 KIO::filesize_t size
= config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE
);
301 if (info
.size
< size
)
302 ::remove(dest_part
.data());
304 error(info
.code
, info
.text
);
308 if (::close(fd
) != 0)
310 error(ERR_COULD_NOT_WRITE
, dest
.prettyUrl());
317 if (::rename(dest_part
.data(), dest_orig
.data()) != 0)
319 error (ERR_CANNOT_RENAME_PARTIAL
, dest_part
);
325 kDebug(KIO_SFTP_DB
) << "sftpCopyGet(): emit finished()";
329 sftpProtocol::Status
sftpProtocol::sftpGet( const KUrl
& src
, KIO::filesize_t offset
, int fd
, bool abortAfterMimeType
)
332 sftpFileAttr
attr(remoteEncoding());
338 kDebug(KIO_SFTP_DB
) << "sftpGet(): " << src
;
340 // stat the file first to get its size
341 if( (code
= sftpStat(src
, attr
)) != SSH2_FX_OK
) {
342 return doProcessStatus(code
, src
.prettyUrl());
345 // We cannot get file if it is a directory
346 if( attr
.fileType() == S_IFDIR
) {
347 res
.text
= src
.prettyUrl();
348 res
.code
= ERR_IS_DIRECTORY
;
352 KIO::filesize_t fileSize
= attr
.fileSize();
353 quint32 pflags
= SSH2_FXF_READ
;
357 if( (code
= sftpOpen(src
, pflags
, attr
, handle
)) != SSH2_FX_OK
) {
358 res
.text
= src
.prettyUrl();
359 res
.code
= ERR_CANNOT_OPEN_FOR_READING
;
363 // needed for determining mimetype
364 // note: have to emit mimetype before emitting totalsize.
366 QByteArray mimeBuffer
;
368 unsigned int oldSize
;
369 bool foundMimetype
= false;
371 // How big should each data packet be? Definitely not bigger than 64kb or
372 // you will overflow the 2 byte size variable in a sftp packet.
373 quint32 len
= 60*1024;
376 kDebug(KIO_SFTP_DB
) << "sftpGet(): offset = " << offset
;
377 while( code
== SSH2_FX_OK
) {
378 if( (code
= sftpRead(handle
, offset
, len
, buff
)) == SSH2_FX_OK
) {
379 offset
+= buff
.size();
381 // save data for mimetype. Pretty much follows what is in the ftp ioslave
382 if( !foundMimetype
) {
383 oldSize
= mimeBuffer
.size();
384 mimeBuffer
.resize(oldSize
+ buff
.size());
385 memcpy(mimeBuffer
.data()+oldSize
, buff
.data(), buff
.size());
387 if( mimeBuffer
.size() > 1024 || offset
== fileSize
) {
388 // determine mimetype
389 KMimeType::Ptr mime
= KMimeType::findByNameAndContent(src
.fileName(), mimeBuffer
);
390 kDebug(KIO_SFTP_DB
) << "sftpGet(): mimetype is " <<
391 mime
->name() << endl
;
392 mimeType(mime
->name());
394 if (abortAfterMimeType
)
397 // Always send the total size after emitting mime-type...
404 if ( (res
.code
=writeToFile(fd
, mimeBuffer
.data(), mimeBuffer
.size())) != 0 )
408 processedSize(mimeBuffer
.size());
409 mimeBuffer
.resize(0);
410 foundMimetype
= true;
418 if ( (res
.code
= writeToFile(fd
, buff
.data(), buff
.size())) != 0 )
421 processedSize(offset
);
426 Check if slave was killed. According to slavebase.h we need to leave
427 the slave methods as soon as possible if the slave is killed. This
428 allows the slave to be cleaned up properly.
431 res
.text
= i18n("An internal error occurred. Please retry the request again.");
432 res
.code
= ERR_UNKNOWN
;
437 if( code
!= SSH2_FX_EOF
&& !abortAfterMimeType
) {
438 res
.text
= src
.prettyUrl();
439 res
.code
= ERR_COULD_NOT_READ
; // return here or still send empty array to indicate end of read?
444 processedSize (offset
);
448 void sftpProtocol::get(const KUrl
& url
) {
449 kDebug(KIO_SFTP_DB
) << "get(): " << url
;
456 quint64 offset
= config()->readEntry("resume",0);
459 kDebug(KIO_SFTP_DB
) << "get(): canResume(), offset = " << offset
;
462 Status info
= sftpGet(url
, offset
);
466 error(info
.code
, info
.text
);
471 kDebug(KIO_SFTP_DB
) << "get(): emit finished()";
476 void sftpProtocol::setHost (const QString
& h
, quint16 port
, const QString
& user
, const QString
& pass
)
478 kDebug(KIO_SFTP_DB
) << "setHost(): " << user
<< "@" << h
<< ":" << port
;
480 if( mHost
!= h
|| mPort
!= port
|| user
!= mUsername
|| mPassword
!= pass
)
489 if( (pse
= getservbyname("ssh", "tcp") ) == NULL
)
492 mPort
= ntohs(pse
->s_port
);
501 mUsername
= u
.loginName();
506 void sftpProtocol::openConnection() {
511 kDebug(KIO_SFTP_DB
) << "openConnection(): " << mUsername
<< "@"
512 << mHost
<< ":" << mPort
<< endl
;
514 infoMessage( i18n("Opening SFTP connection to host %1:%2", mHost
, mPort
));
516 if( mHost
.isEmpty() ) {
517 kDebug(KIO_SFTP_DB
) << "openConnection(): Need hostname...";
518 error(ERR_UNKNOWN_HOST
, i18n("No hostname specified"));
522 ////////////////////////////////////////////////////////////////////////////
523 // Setup AuthInfo for use with password caching and the
524 // password dialog box.
526 info
.url
.setProtocol("sftp");
527 info
.url
.setHost(mHost
);
528 info
.url
.setPort(mPort
);
529 info
.url
.setUser(mUsername
);
530 info
.caption
= i18n("SFTP Login");
531 info
.comment
= "sftp://" + mHost
+ ':' + QString::number(mPort
);
532 info
.commentLabel
= i18n("site:");
533 info
.username
= mUsername
;
534 info
.keepPassword
= true;
536 ///////////////////////////////////////////////////////////////////////////
537 // Check for cached authentication info if a username AND password were
538 // not specified in setHost().
539 if( mUsername
.isEmpty() && mPassword
.isEmpty() ) {
540 kDebug(KIO_SFTP_DB
) << "openConnection(): checking cache "
541 << "info.username = " << info
.username
542 << ", info.url = " << info
.url
.prettyUrl() << endl
;
544 if( checkCachedAuthentication(info
) ) {
545 mUsername
= info
.username
;
546 mPassword
= info
.password
;
550 ///////////////////////////////////////////////////////////////////////////
551 // Now setup our ssh options. If we found a cached username
552 // and password we set the SSH_PASSWORD and SSH_USERNAME
553 // options right away. Otherwise we wait. The other options are
554 // necessary for running sftp over ssh.
555 KSshProcess::SshOpt opt
; // a ssh option, this can be reused
556 KSshProcess::SshOptList opts
; // list of SshOpts
557 KSshProcess::SshOptListIterator passwdIt
; // points to the opt in opts that specifies the password
558 KSshProcess::SshOptListIterator usernameIt
;
560 // opt.opt = KSshProcess::SSH_VERBOSE;
564 // setHost will set a port in any case
565 Q_ASSERT( mPort
!= -1 );
567 opt
.opt
= KSshProcess::SSH_PORT
;
571 opt
.opt
= KSshProcess::SSH_SUBSYSTEM
;
575 opt
.opt
= KSshProcess::SSH_FORWARDX11
;
579 opt
.opt
= KSshProcess::SSH_FORWARDAGENT
;
583 opt
.opt
= KSshProcess::SSH_PROTOCOL
;
587 opt
.opt
= KSshProcess::SSH_HOST
;
591 opt
.opt
= KSshProcess::SSH_ESCAPE_CHAR
;
592 opt
.num
= -1; // don't use any escape character
595 // set the username and password if we have them
596 if( !mUsername
.isEmpty() ) {
597 opt
.opt
= KSshProcess::SSH_USERNAME
;
600 usernameIt
= opts
.end()-1;
603 if( !mPassword
.isEmpty() ) {
604 opt
.opt
= KSshProcess::SSH_PASSWD
;
607 passwdIt
= opts
.end()-1;
610 ssh
.setOptions(opts
);
613 ///////////////////////////////////////////////////////////////////////////
614 // Start the ssh connection process.
617 int err
; // error code from KSshProcess
618 QString msg
; // msg for dialog box
619 QString caption
; // dialog box caption
620 bool firstTime
= true;
623 while( !(mConnected
= ssh
.connect()) ) {
625 kDebug(KIO_SFTP_DB
) << "openConnection(): "
626 "Got " << err
<< " from KSshProcess::connect()" << endl
;
629 case KSshProcess::ERR_NEED_PASSWD
:
630 case KSshProcess::ERR_NEED_PASSPHRASE
:
631 // At this point we know that either we didn't set
632 // an username or password in the ssh options list,
633 // or what we did pass did not work. Therefore we
634 // must prompt the user.
635 if( err
== KSshProcess::ERR_NEED_PASSPHRASE
)
636 info
.prompt
= i18n("Please enter your username and key passphrase.");
638 info
.prompt
= i18n("Please enter your username and password.");
640 kDebug(KIO_SFTP_DB
) << "openConnection(): info.username = " << info
.username
641 << ", info.url = " << info
.url
.prettyUrl() << endl
;
644 dlgResult
= openPasswordDialog(info
);
646 dlgResult
= openPasswordDialog(info
, i18n("Incorrect username or password"));
649 if( info
.username
.isEmpty() || info
.password
.isEmpty() ) {
650 error(ERR_COULD_NOT_AUTHENTICATE
,
651 i18n("Please enter a username and password"));
656 // user canceled or dialog failed to open
657 error(ERR_USER_CANCELED
, QString());
658 kDebug(KIO_SFTP_DB
) << "openConnection(): user canceled, dlgResult = " << dlgResult
;
665 // Check if the username has changed. SSH only accepts
666 // the username at startup. If the username has changed
667 // we must disconnect ssh, change the SSH_USERNAME
668 // option, and reset the option list. We will also set
669 // the password option so the user is not prompted for
671 if( mUsername
!= info
.username
) {
672 kDebug(KIO_SFTP_DB
) << "openConnection(): Username changed from "
673 << mUsername
<< " to " << info
.username
<< endl
;
677 // if we haven't yet added the username
678 // or password option to the ssh options list then
679 // the iterators will be equal to the empty iterator.
680 // Create the opts now and add them to the opt list.
681 if( usernameIt
== KSshProcess::SshOptListIterator() ) {
682 kDebug(KIO_SFTP_DB
) << "openConnection(): "
683 "Adding username to options list" << endl
;
684 opt
.opt
= KSshProcess::SSH_USERNAME
;
686 usernameIt
= opts
.end()-1;
689 if( passwdIt
== KSshProcess::SshOptListIterator() ) {
690 kDebug(KIO_SFTP_DB
) << "openConnection(): "
691 "Adding password to options list" << endl
;
692 opt
.opt
= KSshProcess::SSH_PASSWD
;
694 passwdIt
= opts
.end()-1;
697 (*usernameIt
).str
= info
.username
;
698 (*passwdIt
).str
= info
.password
;
699 ssh
.setOptions(opts
);
702 else { // just set the password
703 ssh
.setPassword(info
.password
);
706 mUsername
= info
.username
;
707 mPassword
= info
.password
;
711 case KSshProcess::ERR_NEW_HOST_KEY
:
712 caption
= i18n("Warning: Cannot verify host's identity.");
713 msg
= ssh
.errorMsg();
714 if( KMessageBox::Yes
!= messageBox(WarningYesNo
, msg
, caption
) ) {
716 error(ERR_USER_CANCELED
, QString());
719 ssh
.acceptHostKey(true);
722 case KSshProcess::ERR_DIFF_HOST_KEY
:
723 caption
= i18n("Warning: Host's identity changed.");
724 msg
= ssh
.errorMsg();
725 if( KMessageBox::Yes
!= messageBox(WarningYesNo
, msg
, caption
) ) {
727 error(ERR_USER_CANCELED
, QString());
730 ssh
.acceptHostKey(true);
733 case KSshProcess::ERR_AUTH_FAILED
:
734 infoMessage(i18n("Authentication failed."));
735 error(ERR_COULD_NOT_LOGIN
, i18n("Authentication failed."));
738 case KSshProcess::ERR_AUTH_FAILED_NEW_KEY
:
739 msg
= ssh
.errorMsg();
740 error(ERR_COULD_NOT_LOGIN
, msg
);
743 case KSshProcess::ERR_AUTH_FAILED_DIFF_KEY
:
744 msg
= ssh
.errorMsg();
745 error(ERR_COULD_NOT_LOGIN
, msg
);
748 case KSshProcess::ERR_CLOSED_BY_REMOTE_HOST
:
749 infoMessage(i18n("Connection failed."));
750 caption
= i18n("Connection closed by remote host.");
751 msg
= ssh
.errorMsg();
752 if (!msg
.isEmpty()) {
757 error(ERR_COULD_NOT_LOGIN
, caption
);
760 case KSshProcess::ERR_INTERACT
:
761 case KSshProcess::ERR_INTERNAL
:
762 case KSshProcess::ERR_UNKNOWN
:
763 case KSshProcess::ERR_INVALID_STATE
:
764 case KSshProcess::ERR_CANNOT_LAUNCH
:
765 case KSshProcess::ERR_HOST_KEY_REJECTED
:
767 infoMessage(i18n("Connection failed."));
768 // Don't call messageBox! Leave GUI handling to the apps (#108812)
769 caption
= i18n("Unexpected SFTP error: %1", err
);
770 msg
= ssh
.errorMsg();
771 if (!msg
.isEmpty()) {
776 error(ERR_UNKNOWN
, caption
);
781 // catch all in case we did something wrong above
783 error(ERR_INTERNAL
, QString());
787 // Now send init packet.
788 kDebug(KIO_SFTP_DB
) << "openConnection(): Sending SSH2_FXP_INIT packet.";
790 QDataStream
packet(&p
, QIODevice::WriteOnly
);
791 packet
<< (quint32
)5; // packet length
792 packet
<< (quint8
) SSH2_FXP_INIT
; // packet type
793 packet
<< (quint32
)SSH2_FILEXFER_VERSION
; // client version
802 kDebug(KIO_SFTP_DB
) << "openConnection(): Got type " << type
;
804 if( type
== SSH2_FXP_VERSION
) {
806 kDebug(KIO_SFTP_DB
) << "openConnection(): Got server version " << version
;
808 // XXX Get extensions here
809 sftpVersion
= version
;
811 /* Server should return lowest common version supported by
812 * client and server, but double check just in case.
814 if( sftpVersion
> SSH2_FILEXFER_VERSION
) {
815 error(ERR_UNSUPPORTED_PROTOCOL
,
816 i18n("SFTP version %1", version
));
822 error(ERR_UNKNOWN
, i18n("Protocol error."));
828 infoMessage(i18n("Successfully connected to %1", mHost
));
829 info
.url
.setProtocol("sftp");
830 info
.url
.setHost(mHost
);
831 info
.url
.setPort(mPort
);
832 info
.url
.setUser(mUsername
);
833 info
.username
= mUsername
;
834 info
.password
= mPassword
;
835 kDebug(KIO_SFTP_DB
) << "sftpProtocol(): caching info.username = " << info
.username
<<
836 ", info.url = " << info
.url
.prettyUrl() << endl
;
837 cacheAuthentication(info
);
842 info
.password
.fill('x');
847 #define _DEBUG kDebug(KIO_SFTP_DB)
849 void sftpProtocol::open(const KUrl
&url
, QIODevice::OpenMode mode
)
851 _DEBUG
<< url
.url() << endl
;
854 error(KIO::ERR_CONNECTION_BROKEN
, url
.prettyUrl());
859 sftpFileAttr
attr(remoteEncoding());
861 if ((code
= sftpStat(url
, attr
)) != SSH2_FX_OK
) {
862 _DEBUG
<< "stat error" << endl
;
863 processStatus(code
, url
.prettyUrl());
867 // don't open a directory
868 if (attr
.fileType() == S_IFDIR
) {
869 _DEBUG
<< "a directory" << endl
;
870 error(KIO::ERR_IS_DIRECTORY
, url
.prettyUrl());
873 if (attr
.fileType() != S_IFREG
) {
874 _DEBUG
<< "not a regular file" << endl
;
875 error(KIO::ERR_CANNOT_OPEN_FOR_READING
, url
.prettyUrl());
879 KIO::filesize_t fileSize
= attr
.fileSize();
884 if (mode
& QIODevice::ReadOnly
) {
885 if (mode
& QIODevice::WriteOnly
) {
886 pflags
= SSH2_FXF_READ
| SSH2_FXF_WRITE
| SSH2_FXF_CREAT
;
888 pflags
= SSH2_FXF_READ
;
890 } else if (mode
& QIODevice::WriteOnly
) {
891 pflags
= SSH2_FXF_WRITE
| SSH2_FXF_CREAT
;
894 if (mode
& QIODevice::Append
) {
895 pflags
|= SSH2_FXF_APPEND
;
896 } else if (mode
& QIODevice::Truncate
) {
897 pflags
|= SSH2_FXF_TRUNC
;
900 code
= sftpOpen(url
, pflags
, attr
, openHandle
);
901 if (code
!= SSH2_FX_OK
) {
902 _DEBUG
<< "sftpOpen error" << endl
;
903 processStatus(code
, url
.prettyUrl());
907 // Determine the mimetype of the file to be retrieved, and emit it.
908 // This is mandatory in all slaves (for KRun/BrowserRun to work).
909 // If we're not opening the file ReadOnly or ReadWrite, don't attempt to
910 // read the file and send the mimetype.
911 if (mode
& QIODevice::ReadOnly
){
913 code
= sftpRead(openHandle
, 0, 1024, buffer
);
914 if ((code
!= SSH2_FX_OK
) && (code
!= SSH2_FX_EOF
)){
915 _DEBUG
<< "error on mime type detection" << endl
;
916 processStatus(code
, url
.prettyUrl());
920 KMimeType::Ptr mime
= KMimeType::findByNameAndContent(url
.fileName(), buffer
);
921 mimeType(mime
->name());
931 void sftpProtocol::read(KIO::filesize_t bytes
)
933 _DEBUG
<< "read, offset = " << openOffset
<< ", bytes = " << bytes
<< endl
;
935 int code
= sftpRead(openHandle
, openOffset
, bytes
, buffer
);
936 if ((code
== SSH2_FX_OK
) || (code
== SSH2_FX_EOF
)) {
937 openOffset
+= buffer
.size();
940 processStatus(code
, openUrl
.prettyUrl());
945 void sftpProtocol::write(const QByteArray
&data
)
947 _DEBUG
<< "write" << endl
;
948 int code
= sftpWrite(openHandle
, openOffset
, data
);
949 if (code
== SSH2_FX_OK
) {
950 openOffset
+= data
.size();
951 written(data
.size());
953 processStatus(code
, openUrl
.prettyUrl());
958 void sftpProtocol::seek(KIO::filesize_t offset
)
960 _DEBUG
<< "seek, offset = " << offset
<< endl
;
965 void sftpProtocol::close()
967 sftpClose(openHandle
);
968 _DEBUG
<< "emitting finished" << endl
;
973 void sftpProtocol::closeConnection() {
974 kDebug(KIO_SFTP_DB
) << "closeConnection()";
979 void sftpProtocol::sftpCopyPut(const KUrl
& src
, const KUrl
& dest
, int permissions
, KIO::JobFlags flags
) {
980 KDE_struct_stat buff
;
981 QByteArray
file (QFile::encodeName(src
.path()));
983 if (KDE_lstat(file
.data(), &buff
) == -1) {
984 error (ERR_DOES_NOT_EXIST
, src
.prettyUrl());
988 if (S_ISDIR (buff
.st_mode
)) {
989 error (ERR_IS_DIRECTORY
, src
.prettyUrl());
993 int fd
= KDE_open (file
.data(), O_RDONLY
);
995 error (ERR_CANNOT_OPEN_FOR_READING
, src
.prettyUrl());
999 totalSize (buff
.st_size
);
1001 sftpPut (dest
, permissions
, (flags
& ~KIO::Resume
), fd
);
1003 // Close the file descriptor...
1007 void sftpProtocol::sftpPut( const KUrl
& dest
, int permissions
, KIO::JobFlags flags
, int fd
) {
1013 kDebug(KIO_SFTP_DB
) << "sftpPut(): " << dest
1014 << ", resume=" << (flags
& KIO::Resume
)
1015 << ", overwrite=" << (flags
& KIO::Overwrite
) << endl
;
1017 KUrl
origUrl( dest
);
1018 sftpFileAttr
origAttr(remoteEncoding());
1019 bool origExists
= false;
1021 // Stat original (without part ext) to see if it already exists
1022 int code
= sftpStat(origUrl
, origAttr
);
1024 if( code
== SSH2_FX_OK
) {
1025 kDebug(KIO_SFTP_DB
) << "sftpPut(): <file> already exists";
1027 // Delete remote file if its size is zero
1028 if( origAttr
.fileSize() == 0 ) {
1029 if( sftpRemove(origUrl
, true) != SSH2_FX_OK
) {
1030 error(ERR_CANNOT_DELETE_ORIGINAL
, origUrl
.prettyUrl());
1038 else if( code
!= SSH2_FX_NO_SUCH_FILE
) {
1039 processStatus(code
, origUrl
.prettyUrl());
1043 // Do not waste time/resources with more remote stat calls if the file exists
1044 // and we weren't instructed to overwrite it...
1045 if( origExists
&& !(flags
& KIO::Overwrite
) ) {
1046 error(ERR_FILE_ALREADY_EXIST
, origUrl
.prettyUrl());
1050 // Stat file with part ext to see if it already exists...
1051 KUrl
partUrl( origUrl
);
1052 partUrl
.setFileName( partUrl
.fileName() + ".part" );
1055 bool partExists
= false;
1056 bool markPartial
= config()->readEntry("MarkPartial", true);
1060 sftpFileAttr
partAttr(remoteEncoding());
1061 code
= sftpStat(partUrl
, partAttr
);
1063 if( code
== SSH2_FX_OK
) {
1064 kDebug(KIO_SFTP_DB
) << "sftpPut(): .part file already exists";
1066 offset
= partAttr
.fileSize();
1068 // If for some reason, both the original and partial files exist,
1069 // skip resumption just like we would if the size of the partial
1071 if( origExists
|| offset
== 0 )
1073 if( sftpRemove(partUrl
, true) != SSH2_FX_OK
) {
1074 error(ERR_CANNOT_DELETE_PARTIAL
, partUrl
.prettyUrl());
1078 if( sftpRename(origUrl
, partUrl
) != SSH2_FX_OK
) {
1079 error(ERR_CANNOT_RENAME_ORIGINAL
, origUrl
.prettyUrl());
1085 else if( !(flags
& KIO::Overwrite
) && !(flags
& KIO::Resume
) ) {
1087 flags
|= (KDE_lseek(fd
, offset
, SEEK_SET
) != -1) ? KIO::Resume
: KIO::DefaultFlags
;
1089 flags
|= canResume( offset
) ? KIO::Resume
: KIO::DefaultFlags
;
1091 kDebug(KIO_SFTP_DB
) << "sftpPut(): can resume = " << (flags
& KIO::Resume
)
1092 << ", offset = " << offset
;
1094 if( !(flags
& KIO::Resume
) ) {
1095 error(ERR_FILE_ALREADY_EXIST
, partUrl
.prettyUrl());
1103 else if( code
== SSH2_FX_NO_SUCH_FILE
) {
1104 if( origExists
&& sftpRename(origUrl
, partUrl
) != SSH2_FX_OK
) {
1105 error(ERR_CANNOT_RENAME_ORIGINAL
, origUrl
.prettyUrl());
1110 processStatus(code
, partUrl
.prettyUrl());
1115 // Determine the url we will actually write to...
1116 KUrl
writeUrl (markPartial
? partUrl
:origUrl
);
1119 if( (flags
& KIO::Overwrite
) && !(flags
& KIO::Resume
) )
1120 pflags
= SSH2_FXF_WRITE
| SSH2_FXF_CREAT
| SSH2_FXF_TRUNC
;
1121 else if( !(flags
& KIO::Overwrite
) && !(flags
& KIO::Resume
) )
1122 pflags
= SSH2_FXF_WRITE
| SSH2_FXF_CREAT
| SSH2_FXF_EXCL
;
1123 else if( (flags
& KIO::Overwrite
) && (flags
& KIO::Resume
) )
1124 pflags
= SSH2_FXF_WRITE
| SSH2_FXF_CREAT
;
1125 else if( !(flags
& KIO::Overwrite
) && (flags
& KIO::Resume
) )
1126 pflags
= SSH2_FXF_WRITE
| SSH2_FXF_CREAT
| SSH2_FXF_APPEND
;
1128 sftpFileAttr
attr(remoteEncoding());
1131 // Set the permissions of the file we write to if it didn't already exist
1132 // and the permission info is supplied, i.e it is not -1
1133 if( !partExists
&& !origExists
&& permissions
!= -1)
1134 attr
.setPermissions(permissions
);
1136 code
= sftpOpen( writeUrl
, pflags
, attr
, handle
);
1137 if( code
!= SSH2_FX_OK
) {
1139 // Rename the file back to its original name if a
1140 // put fails due to permissions problems...
1141 if( markPartial
&& (flags
& KIO::Overwrite
) ) {
1142 (void) sftpRename(partUrl
, origUrl
);
1146 if( code
== SSH2_FX_FAILURE
) { // assume failure means file exists
1147 error(ERR_FILE_ALREADY_EXIST
, writeUrl
.prettyUrl());
1151 processStatus(code
, writeUrl
.prettyUrl());
1162 buff
.resize( 16*1024 );
1163 if ( (nbytes
= ::read(fd
, buff
.data(), buff
.size())) > -1 )
1164 buff
.resize( nbytes
);
1168 nbytes
= readData( buff
);
1172 if( (code
= sftpWrite(handle
, offset
, buff
)) != SSH2_FX_OK
) {
1173 error(ERR_COULD_NOT_WRITE
, dest
.prettyUrl());
1178 processedSize(offset
);
1180 /* Check if slave was killed. According to slavebase.h we
1181 * need to leave the slave methods as soon as possible if
1182 * the slave is killed. This allows the slave to be cleaned
1188 error(ERR_UNKNOWN
, i18n("An internal error occurred. Please try again."));
1193 } while( nbytes
> 0 );
1199 // Remove remote file if it smaller than our keep size
1200 uint minKeepSize
= config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE
);
1202 if( sftpStat(writeUrl
, attr
) == SSH2_FX_OK
) {
1203 if( attr
.fileSize() < minKeepSize
) {
1204 sftpRemove(writeUrl
, true);
1209 error( ERR_UNKNOWN
, i18n("Unknown error was encountered while copying the file "
1210 "to '%1'. Please try again.", dest
.host()) );
1214 if( (code
= sftpClose(handle
)) != SSH2_FX_OK
) {
1215 error(ERR_COULD_NOT_WRITE
, writeUrl
.prettyUrl());
1219 // If wrote to a partial file, then remove the part ext
1221 if( sftpRename(partUrl
, origUrl
) != SSH2_FX_OK
) {
1222 error(ERR_CANNOT_RENAME_PARTIAL
, origUrl
.prettyUrl());
1230 void sftpProtocol::put ( const KUrl
& url
, int permissions
, KIO::JobFlags flags
) {
1231 kDebug(KIO_SFTP_DB
) << "put(): " << url
<< ", overwrite = " << (flags
& KIO::Overwrite
)
1232 << ", resume = " << (flags
& KIO::Resume
) << endl
;
1234 sftpPut( url
, permissions
, flags
);
1237 void sftpProtocol::stat ( const KUrl
& url
){
1238 kDebug(KIO_SFTP_DB
) << "stat(): " << url
;
1244 // If the stat URL has no path, do not attempt to determine the real
1245 // path and do a redirect. KRun will simply ignore such requests.
1246 // Instead, simply return the mime-type as a directory...
1247 if( !url
.hasPath() ) {
1250 entry
.insert( KIO::UDSEntry::UDS_NAME
, QString::fromLatin1(".") );
1251 entry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, S_IFDIR
);
1252 entry
.insert( KIO::UDSEntry::UDS_ACCESS
, S_IRUSR
| S_IXUSR
| S_IRGRP
| S_IXGRP
| S_IROTH
| S_IXOTH
);
1253 entry
.insert( KIO::UDSEntry::UDS_USER
, mUsername
);
1254 entry
.insert( KIO::UDSEntry::UDS_GROUP
, mUsername
);
1263 sftpFileAttr
attr(remoteEncoding());
1264 if( (code
= sftpStat(url
, attr
)) != SSH2_FX_OK
) {
1265 processStatus(code
, url
.prettyUrl());
1269 //kDebug() << "We sent and received stat packet ok";
1270 attr
.setFilename(url
.fileName());
1271 statEntry(attr
.entry());
1276 kDebug(KIO_SFTP_DB
) << "stat: END";
1281 void sftpProtocol::mimetype ( const KUrl
& url
){
1282 kDebug(KIO_SFTP_DB
) << "mimetype(): " << url
;
1288 Status info
= sftpGet(url
, 0 /*offset*/, -1, true /*only emit mimetype*/);
1292 error(info
.code
, info
.text
);
1300 void sftpProtocol::listDir(const KUrl
& url
) {
1301 kDebug(KIO_SFTP_DB
) << "listDir(): " << url
;
1307 if( !url
.hasPath() ) {
1308 KUrl
newUrl ( url
);
1309 if( sftpRealPath(url
, newUrl
) == SSH2_FX_OK
) {
1310 kDebug(KIO_SFTP_DB
) << "listDir: Redirecting to " << newUrl
;
1311 redirection(newUrl
);
1320 if( (code
= sftpOpenDirectory(url
, handle
)) != SSH2_FX_OK
) {
1321 kError(KIO_SFTP_DB
) << "listDir(): open directory failed" << endl
;
1322 processStatus(code
, url
.prettyUrl());
1328 while( code
== SSH2_FX_OK
) {
1329 code
= sftpReadDir(handle
, url
);
1330 if( code
!= SSH2_FX_OK
&& code
!= SSH2_FX_EOF
)
1331 processStatus(code
, url
.prettyUrl());
1332 kDebug(KIO_SFTP_DB
) << "listDir(): return code = " << code
;
1335 if( (code
= sftpClose(handle
)) != SSH2_FX_OK
) {
1336 kError(KIO_SFTP_DB
) << "listdir(): closing of directory failed" << endl
;
1337 processStatus(code
, url
.prettyUrl());
1342 kDebug(KIO_SFTP_DB
) << "listDir(): END";
1345 /** Make a directory.
1346 OpenSSH does not follow the internet draft for sftp in this case.
1347 The format of the mkdir request expected by OpenSSH sftp server is:
1352 void sftpProtocol::mkdir(const KUrl
&url
, int permissions
){
1354 kDebug(KIO_SFTP_DB
) << "mkdir() creating dir: " << url
.path();
1360 QByteArray path
= remoteEncoding()->encode(url
.path());
1361 uint len
= path
.length();
1363 sftpFileAttr
attr(remoteEncoding());
1365 if (permissions
!= -1)
1366 attr
.setPermissions(permissions
);
1368 quint32 id
, expectedId
;
1369 id
= expectedId
= mMsgId
++;
1372 QDataStream
s(&p
, QIODevice::WriteOnly
);
1373 s
<< quint32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
+ attr
.size());
1374 s
<< (quint8
)SSH2_FXP_MKDIR
;
1376 s
.writeBytes(path
.data(), len
);
1379 kDebug(KIO_SFTP_DB
) << "mkdir(): packet size is " << p
.size();
1388 if( id
!= expectedId
) {
1389 kError(KIO_SFTP_DB
) << "mkdir: sftp packet id mismatch" << endl
;
1390 error(ERR_COULD_NOT_MKDIR
, path
);
1395 if( type
!= SSH2_FXP_STATUS
) {
1396 kError(KIO_SFTP_DB
) << "mkdir(): unexpected packet type of " << type
<< endl
;
1397 error(ERR_COULD_NOT_MKDIR
, path
);
1404 if( code
!= SSH2_FX_OK
) {
1405 kError(KIO_SFTP_DB
) << "mkdir(): failed with code " << code
<< endl
;
1407 // Check if mkdir failed because the directory already exists so that
1408 // we can return the appropriate message...
1409 sftpFileAttr
dirAttr(remoteEncoding());
1410 if ( sftpStat(url
, dirAttr
) == SSH2_FX_OK
)
1412 error( ERR_DIR_ALREADY_EXIST
, url
.prettyUrl() );
1416 error(ERR_COULD_NOT_MKDIR
, path
);
1422 void sftpProtocol::rename(const KUrl
& src
, const KUrl
& dest
, KIO::JobFlags flags
) {
1423 kDebug(KIO_SFTP_DB
) << "rename(" << src
<< " -> " << dest
<< ")";
1425 if (!isSupportedOperation(SSH2_FXP_RENAME
)) {
1426 error(ERR_UNSUPPORTED_ACTION
,
1427 i18n("The remote host does not support renaming files."));
1435 // Always stat the destination before attempting to rename
1436 // a file or a directory...
1437 sftpFileAttr
attr(remoteEncoding());
1438 int code
= sftpStat(dest
, attr
);
1440 // If the destination directory, exists tell it to the job
1441 // so it the proper action can be presented to the user...
1442 if( code
== SSH2_FX_OK
)
1444 if (!(flags
& KIO::Overwrite
))
1446 if ( S_ISDIR(attr
.permissions()) )
1447 error( KIO::ERR_DIR_ALREADY_EXIST
, dest
.url() );
1449 error( KIO::ERR_FILE_ALREADY_EXIST
, dest
.url() );
1453 // If overwrite is specified, then simply remove the existing file/dir first...
1454 if( (code
= sftpRemove( dest
, !S_ISDIR(attr
.permissions()) )) != SSH2_FX_OK
)
1456 processStatus(code
);
1461 // Do the renaming...
1462 if( (code
= sftpRename(src
, dest
)) != SSH2_FX_OK
) {
1463 processStatus(code
);
1468 kDebug(KIO_SFTP_DB
) << "rename(): END";
1471 void sftpProtocol::symlink(const QString
& target
, const KUrl
& dest
, KIO::JobFlags flags
) {
1472 kDebug(KIO_SFTP_DB
) << "symlink()";
1474 if (!isSupportedOperation(SSH2_FXP_SYMLINK
)) {
1475 error(ERR_UNSUPPORTED_ACTION
,
1476 i18n("The remote host does not support creating symbolic links."));
1485 bool failed
= false;
1486 if( (code
= sftpSymLink(target
, dest
)) != SSH2_FX_OK
) {
1487 if( flags
& KIO::Overwrite
) { // try to delete the destination
1488 sftpFileAttr
attr(remoteEncoding());
1489 if( (code
= sftpStat(dest
, attr
)) != SSH2_FX_OK
) {
1493 if( (code
= sftpRemove(dest
, !S_ISDIR(attr
.permissions())) ) != SSH2_FX_OK
) {
1497 // XXX what if rename fails again? We have lost the file.
1498 // Maybe rename dest to a temporary name first? If rename is
1499 // successful, then delete?
1500 if( (code
= sftpSymLink(target
, dest
)) != SSH2_FX_OK
)
1505 else if( code
== SSH2_FX_FAILURE
) {
1506 error(ERR_FILE_ALREADY_EXIST
, dest
.prettyUrl());
1513 // What error code do we return? Code for the original symlink command
1514 // or for the last command or for both? The second one is implemented here.
1516 processStatus(code
);
1521 void sftpProtocol::chmod(const KUrl
& url
, int permissions
){
1523 perms
.setNum(permissions
, 8);
1524 kDebug(KIO_SFTP_DB
) << "chmod(" << url
<< ", " << perms
<< ")";
1530 sftpFileAttr
attr(remoteEncoding());
1532 if (permissions
!= -1)
1533 attr
.setPermissions(permissions
);
1536 if( (code
= sftpSetStat(url
, attr
)) != SSH2_FX_OK
) {
1537 kError(KIO_SFTP_DB
) << "chmod(): sftpSetStat failed with error " << code
<< endl
;
1538 if( code
== SSH2_FX_FAILURE
)
1539 error(ERR_CANNOT_CHMOD
, QString());
1541 processStatus(code
, url
.prettyUrl());
1547 void sftpProtocol::del(const KUrl
&url
, bool isfile
){
1548 kDebug(KIO_SFTP_DB
) << "del(" << url
<< ", " << (isfile
?"file":"dir") << ")";
1555 if( (code
= sftpRemove(url
, isfile
)) != SSH2_FX_OK
) {
1556 kError(KIO_SFTP_DB
) << "del(): sftpRemove failed with error code " << code
<< endl
;
1557 processStatus(code
, url
.prettyUrl());
1562 void sftpProtocol::slave_status() {
1563 kDebug(KIO_SFTP_DB
) << "slave_status(): connected to "
1564 << mHost
<< "? " << mConnected
<< endl
;
1566 slaveStatus ((mConnected
? mHost
: QString()), mConnected
);
1569 bool sftpProtocol::getPacket(QByteArray
& msg
) {
1570 QByteArray
buf(4096, '\0');
1574 if(ssh
.pty()->waitForReadyRead(2000)) {
1575 len
= ssh
.pty()->read(buf
.data(), 4);
1578 // Get the message length...
1579 ssize_t len
= atomicio(ssh
.stdioFd(), buf
.data(), 4, true /*read*/);
1581 if( len
== 0 || len
== -1 ) {
1582 kDebug(KIO_SFTP_DB
) << "getPacket(): read of packet length failed, ret = "
1583 << len
<< ", error =" << strerror(errno
) << endl
;
1585 error( ERR_CONNECTION_BROKEN
, mHost
);
1594 //kDebug(KIO_SFTP_DB) << "getPacket(): Message size = " << msgLen;
1599 b
.open( QIODevice::WriteOnly
);
1603 len
= ssh
.pty()->read(buf
.data(), qMin(buf
.size(), msgLen
));
1605 len
= atomicio(ssh
.stdioFd(), buf
.data(), qMin(buf
.size(), msgLen
), true /*read*/);
1608 if( len
== 0 || len
== -1) {
1611 errmsg
= i18n("Connection closed");
1613 errmsg
= i18n("Could not read SFTP packet");
1614 kDebug(KIO_SFTP_DB
) << "getPacket(): nothing to read, ret = " <<
1615 len
<< ", error =" << strerror(errno
) << endl
;
1617 error(ERR_CONNECTION_BROKEN
, errmsg
);
1622 b
.write(buf
.data(), len
);
1624 //kDebug(KIO_SFTP_DB) << "getPacket(): Read Message size = " << len;
1625 //kDebug(KIO_SFTP_DB) << "getPacket(): Copy Message size = " << msg.size();
1635 /** Send an sftp packet to stdin of the ssh process. */
1636 bool sftpProtocol::putPacket(QByteArray
& p
){
1637 // kDebug(KIO_SFTP_DB) << "putPacket(): size == " << p.size();
1640 ret
= ssh
.pty()->write(p
.data(), p
.size());
1642 ret
= atomicio(ssh
.stdioFd(), p
.data(), p
.size(), false /*write*/);
1645 kDebug(KIO_SFTP_DB
) << "putPacket(): write failed, ret =" << ret
<<
1646 ", error = " << strerror(errno
) << endl
;
1653 /** Used to have the server canonicalize any given path name to an absolute path.
1654 This is useful for converting path names containing ".." components or relative
1655 pathnames without a leading slash into absolute paths.
1656 Returns the canonicalized url. */
1657 int sftpProtocol::sftpRealPath(const KUrl
& url
, KUrl
& newUrl
){
1659 kDebug(KIO_SFTP_DB
) << "sftpRealPath(" << url
<< ", newUrl)";
1661 QByteArray path
= remoteEncoding()->encode(url
.path());
1662 uint len
= path
.length();
1664 quint32 id
, expectedId
;
1665 id
= expectedId
= mMsgId
++;
1668 QDataStream
s(&p
, QIODevice::WriteOnly
);
1669 s
<< quint32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
);
1670 s
<< (quint8
)SSH2_FXP_REALPATH
;
1672 s
.writeBytes(path
.data(), len
);
1681 if( id
!= expectedId
) {
1682 kError(KIO_SFTP_DB
) << "sftpRealPath: sftp packet id mismatch" << endl
;
1686 if( type
== SSH2_FXP_STATUS
) {
1692 if( type
!= SSH2_FXP_NAME
) {
1693 kError(KIO_SFTP_DB
) << "sftpRealPath(): unexpected packet type of " << type
<< endl
;
1700 kError(KIO_SFTP_DB
) << "sftpRealPath(): Bad number of file attributes for realpath command" << endl
;
1707 newPath
.truncate(newPath
.size());
1708 if (newPath
.isEmpty())
1710 newUrl
.setPath(newPath
);
1715 sftpProtocol::Status
sftpProtocol::doProcessStatus(quint8 code
, const QString
& message
)
1726 res
.text
= i18n("End of file.");
1728 case SSH2_FX_NO_SUCH_FILE
:
1729 res
.code
= ERR_DOES_NOT_EXIST
;
1731 case SSH2_FX_PERMISSION_DENIED
:
1732 res
.code
= ERR_ACCESS_DENIED
;
1734 case SSH2_FX_FAILURE
:
1735 res
.text
= i18n("SFTP command failed for an unknown reason.");
1736 res
.code
= ERR_UNKNOWN
;
1738 case SSH2_FX_BAD_MESSAGE
:
1739 res
.text
= i18n("The SFTP server received a bad message.");
1740 res
.code
= ERR_UNKNOWN
;
1742 case SSH2_FX_OP_UNSUPPORTED
:
1743 res
.text
= i18n("You attempted an operation unsupported by the SFTP server.");
1744 res
.code
= ERR_UNKNOWN
;
1747 res
.text
= i18n("Error code: %1", code
);
1748 res
.code
= ERR_UNKNOWN
;
1754 /** Process SSH_FXP_STATUS packets. */
1755 void sftpProtocol::processStatus(quint8 code
, const QString
& message
){
1756 Status st
= doProcessStatus( code
, message
);
1758 error( st
.code
, st
.text
);
1762 /** Opens a directory handle for url.path. Returns true if succeeds. */
1763 int sftpProtocol::sftpOpenDirectory(const KUrl
& url
, QByteArray
& handle
){
1765 kDebug(KIO_SFTP_DB
) << "sftpOpenDirectory(" << url
<< ", handle)";
1767 QByteArray path
= remoteEncoding()->encode(url
.path());
1768 uint len
= path
.length();
1770 quint32 id
, expectedId
;
1771 id
= expectedId
= mMsgId
++;
1774 QDataStream
s(&p
, QIODevice::WriteOnly
);
1775 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
);
1776 s
<< (quint8
)SSH2_FXP_OPENDIR
;
1778 s
.writeBytes(path
.data(), len
);
1787 if( id
!= expectedId
) {
1788 kError(KIO_SFTP_DB
) << "sftpOpenDirectory: sftp packet id mismatch: " <<
1789 "expected " << expectedId
<< ", got " << id
<< endl
;
1793 if( type
== SSH2_FXP_STATUS
) {
1799 if( type
!= SSH2_FXP_HANDLE
) {
1800 kError(KIO_SFTP_DB
) << "sftpOpenDirectory: unexpected message type of " << type
<< endl
;
1805 if( handle
.size() > 256 ) {
1806 kError(KIO_SFTP_DB
) << "sftpOpenDirectory: handle exceeds max length" << endl
;
1810 kDebug(KIO_SFTP_DB
) << "sftpOpenDirectory: handle (" << handle
.size() << "): [" << handle
<< "]";
1814 /** Closes a directory or file handle. */
1815 int sftpProtocol::sftpClose(const QByteArray
& handle
){
1817 kDebug(KIO_SFTP_DB
) << "sftpClose()";
1819 quint32 id
, expectedId
;
1820 id
= expectedId
= mMsgId
++;
1823 QDataStream
s(&p
, QIODevice::WriteOnly
);
1824 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle
.size());
1825 s
<< (quint8
)SSH2_FXP_CLOSE
;
1836 if( id
!= expectedId
) {
1837 kError(KIO_SFTP_DB
) << "sftpClose: sftp packet id mismatch" << endl
;
1841 if( type
!= SSH2_FXP_STATUS
) {
1842 kError(KIO_SFTP_DB
) << "sftpClose: unexpected message type of " << type
<< endl
;
1848 if( code
!= SSH2_FX_OK
) {
1849 kError(KIO_SFTP_DB
) << "sftpClose: close failed with err code " << code
<< endl
;
1855 /** Set a files attributes. */
1856 int sftpProtocol::sftpSetStat(const KUrl
& url
, const sftpFileAttr
& attr
){
1858 kDebug(KIO_SFTP_DB
) << "sftpSetStat(" << url
<< ", attr)";
1860 QByteArray path
= remoteEncoding()->encode(url
.path());
1861 uint len
= path
.length();
1863 quint32 id
, expectedId
;
1864 id
= expectedId
= mMsgId
++;
1867 QDataStream
s(&p
, QIODevice::WriteOnly
);
1868 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
+ attr
.size());
1869 s
<< (quint8
)SSH2_FXP_SETSTAT
;
1871 s
.writeBytes(path
.data(), len
);
1881 if( id
!= expectedId
) {
1882 kError(KIO_SFTP_DB
) << "sftpSetStat(): sftp packet id mismatch" << endl
;
1884 // XXX How do we do a fatal error?
1887 if( type
!= SSH2_FXP_STATUS
) {
1888 kError(KIO_SFTP_DB
) << "sftpSetStat(): unexpected message type of " << type
<< endl
;
1894 if( code
!= SSH2_FX_OK
) {
1895 kError(KIO_SFTP_DB
) << "sftpSetStat(): set stat failed with err code " << code
<< endl
;
1901 /** Sends a sftp command to remove a file or directory. */
1902 int sftpProtocol::sftpRemove(const KUrl
& url
, bool isfile
){
1904 kDebug(KIO_SFTP_DB
) << "sftpRemove(): " << url
<< ", isFile ? " << isfile
;
1906 QByteArray path
= remoteEncoding()->encode(url
.path());
1907 uint len
= path
.length();
1909 quint32 id
, expectedId
;
1910 id
= expectedId
= mMsgId
++;
1913 QDataStream
s(&p
, QIODevice::WriteOnly
);
1914 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
);
1915 s
<< (quint8
)(isfile
? SSH2_FXP_REMOVE
: SSH2_FXP_RMDIR
);
1917 s
.writeBytes(path
.data(), len
);
1926 if( id
!= expectedId
) {
1927 kError(KIO_SFTP_DB
) << "del(): sftp packet id mismatch" << endl
;
1931 if( type
!= SSH2_FXP_STATUS
) {
1932 kError(KIO_SFTP_DB
) << "del(): unexpected message type of " << type
<< endl
;
1938 if( code
!= SSH2_FX_OK
) {
1939 kError(KIO_SFTP_DB
) << "del(): del failed with err code " << code
<< endl
;
1945 /** Send a sftp command to rename a file or directory. */
1946 int sftpProtocol::sftpRename(const KUrl
& src
, const KUrl
& dest
){
1948 kDebug(KIO_SFTP_DB
) << "sftpRename(" << src
<< " -> " << dest
<< ")";
1950 QByteArray srcPath
= remoteEncoding()->encode(src
.path());
1951 QByteArray destPath
= remoteEncoding()->encode(dest
.path());
1953 uint slen
= srcPath
.length();
1954 uint dlen
= destPath
.length();
1956 quint32 id
, expectedId
;
1957 id
= expectedId
= mMsgId
++;
1960 QDataStream
s(&p
, QIODevice::WriteOnly
);
1961 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ +
1962 4 /*str length*/ + slen
+
1963 4 /*str length*/ + dlen
);
1964 s
<< (quint8
)SSH2_FXP_RENAME
;
1966 s
.writeBytes(srcPath
.data(), slen
);
1967 s
.writeBytes(destPath
.data(), dlen
);
1976 if( id
!= expectedId
) {
1977 kError(KIO_SFTP_DB
) << "sftpRename(): sftp packet id mismatch" << endl
;
1981 if( type
!= SSH2_FXP_STATUS
) {
1982 kError(KIO_SFTP_DB
) << "sftpRename(): unexpected message type of " << type
<< endl
;
1988 if( code
!= SSH2_FX_OK
) {
1989 kError(KIO_SFTP_DB
) << "sftpRename(): rename failed with err code " << code
<< endl
;
1994 /** Get directory listings. */
1995 int sftpProtocol::sftpReadDir(const QByteArray
& handle
, const KUrl
& url
){
1996 // url is needed so we can lookup the link destination
1997 kDebug(KIO_SFTP_DB
) << "sftpReadDir(): " << url
;
1999 quint32 id
, expectedId
, count
;
2002 sftpFileAttr
attr (remoteEncoding());
2003 attr
.setDirAttrsFlag(true);
2006 QDataStream
s(&p
, QIODevice::WriteOnly
);
2007 id
= expectedId
= mMsgId
++;
2008 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle
.size());
2009 s
<< (quint8
)SSH2_FXP_READDIR
;
2019 if( id
!= expectedId
) {
2020 kError(KIO_SFTP_DB
) << "sftpReadDir(): sftp packet id mismatch" << endl
;
2025 if( type
== SSH2_FXP_STATUS
) {
2030 if( type
!= SSH2_FXP_NAME
) {
2031 kError(KIO_SFTP_DB
) << "kio_sftpProtocol::sftpReadDir(): Unexpected message" << endl
;
2036 kDebug(KIO_SFTP_DB
) << "sftpReadDir(): got " << count
<< " entries";
2041 if( S_ISLNK(attr
.permissions()) ) {
2043 myurl
.addPath(attr
.filename());
2045 // Stat the symlink to find out its type...
2046 sftpFileAttr
attr2 (remoteEncoding());
2047 (void) sftpStat(myurl
, attr2
);
2049 attr
.setLinkType(attr2
.linkType());
2050 attr
.setLinkDestination(attr2
.linkDestination());
2053 listEntry(attr
.entry(), false);
2056 listEntry(attr
.entry(), true);
2061 int sftpProtocol::sftpReadLink(const KUrl
& url
, QString
& target
){
2063 kDebug(KIO_SFTP_DB
) << "sftpReadLink(): " << url
;
2065 QByteArray path
= remoteEncoding()->encode(url
.path());
2066 uint len
= path
.length();
2068 //kDebug(KIO_SFTP_DB) << "sftpReadLink(): Encoded Path: " << path;
2069 //kDebug(KIO_SFTP_DB) << "sftpReadLink(): Encoded Size: " << len;
2071 quint32 id
, expectedId
;
2072 id
= expectedId
= mMsgId
++;
2075 QDataStream
s(&p
, QIODevice::WriteOnly
);
2076 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
);
2077 s
<< (quint8
)SSH2_FXP_READLINK
;
2079 s
.writeBytes(path
.data(), len
);
2089 if( id
!= expectedId
) {
2090 kError(KIO_SFTP_DB
) << "sftpReadLink(): sftp packet id mismatch" << endl
;
2094 if( type
== SSH2_FXP_STATUS
) {
2097 kDebug(KIO_SFTP_DB
) << "sftpReadLink(): read link failed with code " << code
;
2101 if( type
!= SSH2_FXP_NAME
) {
2102 kError(KIO_SFTP_DB
) << "sftpReadLink(): unexpected packet type of " << type
<< endl
;
2109 kError(KIO_SFTP_DB
) << "sftpReadLink(): Bad number of file attributes for realpath command" << endl
;
2113 QByteArray linkAddress
;
2116 linkAddress
.truncate(linkAddress
.size());
2117 kDebug(KIO_SFTP_DB
) << "sftpReadLink(): Link address: " << linkAddress
;
2119 target
= remoteEncoding()->decode(linkAddress
);
2124 int sftpProtocol::sftpSymLink(const QString
& _target
, const KUrl
& dest
){
2126 QByteArray destPath
= remoteEncoding()->encode(dest
.path());
2127 QByteArray target
= remoteEncoding()->encode(_target
);
2128 uint dlen
= destPath
.length();
2129 uint tlen
= target
.length();
2131 kDebug(KIO_SFTP_DB
) << "sftpSymLink(" << target
<< " -> " << destPath
<< ")";
2133 quint32 id
, expectedId
;
2134 id
= expectedId
= mMsgId
++;
2137 QDataStream
s(&p
, QIODevice::WriteOnly
);
2138 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ +
2139 4 /*str length*/ + tlen
+
2140 4 /*str length*/ + dlen
);
2141 s
<< (quint8
)SSH2_FXP_SYMLINK
;
2143 s
.writeBytes(target
.data(), tlen
);
2144 s
.writeBytes(destPath
.data(), dlen
);
2153 if( id
!= expectedId
) {
2154 kError(KIO_SFTP_DB
) << "sftpSymLink(): sftp packet id mismatch" << endl
;
2158 if( type
!= SSH2_FXP_STATUS
) {
2159 kError(KIO_SFTP_DB
) << "sftpSymLink(): unexpected message type of " << type
<< endl
;
2165 if( code
!= SSH2_FX_OK
) {
2166 kError(KIO_SFTP_DB
) << "sftpSymLink(): rename failed with err code " << code
<< endl
;
2172 /** Stats a file. */
2173 int sftpProtocol::sftpStat(const KUrl
& url
, sftpFileAttr
& attr
) {
2175 kDebug(KIO_SFTP_DB
) << "sftpStat(): " << url
;
2177 QByteArray path
= remoteEncoding()->encode(url
.path());
2178 uint len
= path
.length();
2180 quint32 id
, expectedId
;
2181 id
= expectedId
= mMsgId
++;
2184 QDataStream
s(&p
, QIODevice::WriteOnly
);
2185 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len
);
2186 s
<< (quint8
)SSH2_FXP_LSTAT
;
2188 s
.writeBytes(path
.data(), len
);
2197 if( id
!= expectedId
) {
2198 kError(KIO_SFTP_DB
) << "sftpStat(): sftp packet id mismatch" << endl
;
2202 if( type
== SSH2_FXP_STATUS
) {
2205 kError(KIO_SFTP_DB
) << "sftpStat(): stat failed with code " << errCode
<< endl
;
2209 if( type
!= SSH2_FXP_ATTRS
) {
2210 kError(KIO_SFTP_DB
) << "sftpStat(): unexpected message type of " << type
<< endl
;
2215 attr
.setFilename(url
.fileName());
2216 kDebug(KIO_SFTP_DB
) << "sftpStat(): " << attr
;
2218 // If the stat'ed resource is a symlink, perform a recursive stat
2219 // to determine the actual destination's type (file/dir).
2220 if( S_ISLNK(attr
.permissions()) && isSupportedOperation(SSH2_FXP_READLINK
) ) {
2223 int code
= sftpReadLink( url
, target
);
2225 if ( code
!= SSH2_FX_OK
) {
2226 kError(KIO_SFTP_DB
) << "sftpStat(): Unable to stat symlink destination" << endl
;
2230 kDebug(KIO_SFTP_DB
) << "sftpStat(): Resource is a symlink to -> " << target
;
2233 if( target
[0] == '/' )
2234 dest
.setPath(target
);
2236 dest
.setFileName(target
);
2240 // Ignore symlinks that point to themselves...
2241 if ( dest
!= url
) {
2243 sftpFileAttr
attr2 (remoteEncoding());
2244 (void) sftpStat(dest
, attr2
);
2246 if (attr2
.linkType() == 0)
2247 attr
.setLinkType(attr2
.fileType());
2249 attr
.setLinkType(attr2
.linkType());
2251 attr
.setLinkDestination(target
);
2253 kDebug(KIO_SFTP_DB
) << "sftpStat(): File type: " << attr
.fileType();
2261 int sftpProtocol::sftpOpen(const KUrl
& url
, const quint32 pflags
,
2262 const sftpFileAttr
& attr
, QByteArray
& handle
) {
2263 kDebug(KIO_SFTP_DB
) << "sftpOpen(" << url
<< ", handle";
2265 QByteArray path
= remoteEncoding()->encode(url
.path());
2266 uint len
= path
.length();
2268 quint32 id
, expectedId
;
2269 id
= expectedId
= mMsgId
++;
2272 QDataStream
s(&p
, QIODevice::WriteOnly
);
2273 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ +
2274 4 /*str length*/ + len
+
2275 4 /*pflags*/ + attr
.size());
2276 s
<< (quint8
)SSH2_FXP_OPEN
;
2278 s
.writeBytes(path
.data(), len
);
2289 if( id
!= expectedId
) {
2290 kError(KIO_SFTP_DB
) << "sftpOpen(): sftp packet id mismatch" << endl
;
2294 if( type
== SSH2_FXP_STATUS
) {
2300 if( type
!= SSH2_FXP_HANDLE
) {
2301 kError(KIO_SFTP_DB
) << "sftpOpen(): unexpected message type of " << type
<< endl
;
2306 if( handle
.size() > 256 ) {
2307 kError(KIO_SFTP_DB
) << "sftpOpen(): handle exceeds max length" << endl
;
2311 kDebug(KIO_SFTP_DB
) << "sftpOpen(): handle (" << handle
.size() << "): [" << handle
<< "]";
2316 int sftpProtocol::sftpRead(const QByteArray
& handle
, KIO::filesize_t offset
, quint32 len
, QByteArray
& data
)
2318 // kDebug(KIO_SFTP_DB) << "sftpRead( offset = " << offset << ", len = " << len << ")";
2320 QDataStream
s(&p
, QIODevice::WriteOnly
);
2322 quint32 id
, expectedId
;
2323 id
= expectedId
= mMsgId
++;
2324 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ +
2325 4 /*str length*/ + handle
.size() +
2326 8 /*offset*/ + 4 /*length*/);
2327 s
<< (quint8
)SSH2_FXP_READ
;
2330 s
<< offset
; // we don't have a convienient 64 bit int so set upper int to zero
2340 if( id
!= expectedId
) {
2341 kError(KIO_SFTP_DB
) << "sftpRead: sftp packet id mismatch" << endl
;
2345 if( type
== SSH2_FXP_STATUS
) {
2348 kError(KIO_SFTP_DB
) << "sftpRead: read failed with code " << errCode
<< endl
;
2352 if( type
!= SSH2_FXP_DATA
) {
2353 kError(KIO_SFTP_DB
) << "sftpRead: unexpected message type of " << type
<< endl
;
2363 int sftpProtocol::sftpWrite(const QByteArray
& handle
, KIO::filesize_t offset
, const QByteArray
& data
){
2364 // kDebug(KIO_SFTP_DB) << "sftpWrite( offset = " << offset <<
2365 // ", data sz = " << data.size() << ")" << endl;
2367 QDataStream
s(&p
, QIODevice::WriteOnly
);
2369 quint32 id
, expectedId
;
2370 id
= expectedId
= mMsgId
++;
2371 s
<< (quint32
)(1 /*type*/ + 4 /*id*/ +
2372 4 /*str length*/ + handle
.size() +
2374 4 /* data size */ + data
.size());
2375 s
<< (quint8
)SSH2_FXP_WRITE
;
2378 s
<< offset
; // we don't have a convienient 64 bit int so set upper int to zero
2381 // kDebug(KIO_SFTP_DB) << "sftpWrite(): SSH2_FXP_WRITE, id:"
2382 // << id << ", handle:" << handle << ", offset:" << offset << ", some data" << endl;
2384 // kDebug(KIO_SFTP_DB) << "sftpWrite(): send packet [" << p << "]";
2389 // kDebug(KIO_SFTP_DB) << "sftpWrite(): received packet [" << p << "]";
2395 if( id
!= expectedId
) {
2396 kError(KIO_SFTP_DB
) << "sftpWrite(): sftp packet id mismatch, got "
2397 << id
<< ", expected " << expectedId
<< endl
;
2401 if( type
!= SSH2_FXP_STATUS
) {
2402 kError(KIO_SFTP_DB
) << "sftpWrite(): unexpected message type of " << type
<< endl
;