add more spacing
[personal-kdebase.git] / runtime / kioslave / sftp / kio_sftp.cpp
blobe485b02ce8aa3273f864fd7d39fa246f7c2acfc1
1 /***************************************************************************
2 sftp.cpp - description
3 -------------------
4 begin : Fri Jun 29 23:45:40 CDT 2001
5 copyright : (C) 2001 by Lucas Fisher
6 email : ljfisher@purdue.edu
7 ***************************************************************************/
9 /***************************************************************************
10 * *
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. *
15 * *
16 ***************************************************************************/
19 DEBUGGING
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.
23 So we can't connect.
26 #include "kio_sftp.h"
28 #include <config-runtime.h>
30 #include <fcntl.h>
32 #include <QtCore/QBuffer>
33 #include <QtCore/QByteArray>
34 #include <QtCore/QFile>
35 #include <QtCore/QObject>
36 #include <QtCore/QString>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <signal.h>
41 #include <errno.h>
42 #include <ctype.h>
43 #include <time.h>
44 #include <netdb.h>
45 #include <string.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <sys/time.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #include <sys/wait.h>
54 #include <kapplication.h>
55 #include <kuser.h>
56 #include <kdebug.h>
57 #include <kmessagebox.h>
58 #include <kcomponentdata.h>
59 #include <kglobal.h>
60 #include <kstandarddirs.h>
61 #include <klocale.h>
62 #include <kurl.h>
63 #include <kio/ioslave_defaults.h>
64 #include <kmimetype.h>
65 #include <kde_file.h>
66 #include <kremoteencoding.h>
67 #include <kconfiggroup.h>
69 #include "sftp.h"
70 #include "atomicio.h"
71 #include "sftpfileattr.h"
72 #include "ksshprocess.h"
75 using namespace KIO;
76 extern "C"
78 int KDE_EXPORT kdemain( int argc, char **argv )
80 KComponentData componentData( "kio_sftp" );
82 kDebug(KIO_SFTP_DB) << "*** Starting kio_sftp ";
84 if (argc != 4) {
85 kDebug(KIO_SFTP_DB) << "Usage: kio_sftp protocol domain-socket1 domain-socket2";
86 exit(-1);
89 sftpProtocol slave(argv[2], argv[3]);
90 slave.dispatchLoop();
92 kDebug(KIO_SFTP_DB) << "*** kio_sftp Done";
93 return 0;
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)
107 while (len > 0)
109 ssize_t written = ::write(fd, buf, len);
110 if (written >= 0)
112 buf += written;
113 len -= written;
114 continue;
117 switch(errno)
119 case EINTR:
120 continue;
121 case EPIPE:
122 return ERR_CONNECTION_BROKEN;
123 case ENOSPC:
124 return ERR_DISK_FULL;
125 default:
126 return ERR_COULD_NOT_WRITE;
129 return 0;
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) {
135 #ifndef Q_WS_WIN
136 kDebug(KIO_SFTP_DB) << "sftpProtocol(): pid = " << getpid();
137 #endif
141 sftpProtocol::~sftpProtocol() {
142 #ifndef Q_WS_WIN
143 kDebug(KIO_SFTP_DB) << "~sftpProtocol(): pid = " << getpid();
144 #endif
145 closeConnection();
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) {
156 switch (type) {
157 case SSH2_FXP_VERSION:
158 case SSH2_FXP_STATUS:
159 case SSH2_FXP_HANDLE:
160 case SSH2_FXP_DATA:
161 case SSH2_FXP_NAME:
162 case SSH2_FXP_ATTRS:
163 case SSH2_FXP_INIT:
164 case SSH2_FXP_OPEN:
165 case SSH2_FXP_CLOSE:
166 case SSH2_FXP_READ:
167 case SSH2_FXP_WRITE:
168 case SSH2_FXP_LSTAT:
169 case SSH2_FXP_FSTAT:
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:
175 case SSH2_FXP_MKDIR:
176 case SSH2_FXP_RMDIR:
177 case SSH2_FXP_REALPATH:
178 case SSH2_FXP_STAT:
179 return true;
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;
187 default:
188 kDebug(KIO_SFTP_DB) << "isSupportedOperation(type:"
189 << type << "): unrecognized operation type" << endl;
190 break;
193 return false;
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);
207 else
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...
216 openConnection();
217 if( !mConnected )
218 return;
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);
224 if (origExists)
226 if (S_ISDIR(buff_orig.st_mode))
228 error(ERR_IS_DIRECTORY, dest.prettyUrl());
229 return;
232 if (!(flags & KIO::Overwrite))
234 error(ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
235 return;
239 KIO::filesize_t offset = 0;
240 QByteArray dest_part ( dest_orig + ".part" );
242 int fd = -1;
243 bool partExists = false;
244 bool markPartial = config()->readEntry("MarkPartial", true);
246 if (markPartial)
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;
260 if (offset > 0)
262 fd = KDE_open(dest_part.data(), O_RDWR);
263 offset = KDE_lseek(fd, 0, SEEK_END);
264 if (offset == 0)
266 error(ERR_CANNOT_RESUME, dest.prettyUrl());
267 return;
270 else
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);
278 else
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);
286 if(fd == -1)
288 kDebug(KIO_SFTP_DB) << "sftpCopyGet: Unable to open (" << fd << ") for writing.";
289 if (errno == EACCES)
290 error (ERR_WRITE_ACCESS_DENIED, dest.prettyUrl());
291 else
292 error (ERR_CANNOT_OPEN_FOR_WRITING, dest.prettyUrl());
293 return;
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);
305 return;
308 if (::close(fd) != 0)
310 error(ERR_COULD_NOT_WRITE, dest.prettyUrl());
311 return;
315 if (markPartial)
317 if (::rename(dest_part.data(), dest_orig.data()) != 0)
319 error (ERR_CANNOT_RENAME_PARTIAL, dest_part);
320 return;
324 data(QByteArray());
325 kDebug(KIO_SFTP_DB) << "sftpCopyGet(): emit finished()";
326 finished();
329 sftpProtocol::Status sftpProtocol::sftpGet( const KUrl& src, KIO::filesize_t offset, int fd, bool abortAfterMimeType )
331 int code;
332 sftpFileAttr attr(remoteEncoding());
334 Status res;
335 res.code = 0;
336 res.size = 0;
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;
349 return res;
352 KIO::filesize_t fileSize = attr.fileSize();
353 quint32 pflags = SSH2_FXF_READ;
354 attr.clear();
356 QByteArray handle;
357 if( (code = sftpOpen(src, pflags, attr, handle)) != SSH2_FX_OK ) {
358 res.text = src.prettyUrl();
359 res.code = ERR_CANNOT_OPEN_FOR_READING;
360 return res;
363 // needed for determining mimetype
364 // note: have to emit mimetype before emitting totalsize.
365 QByteArray buff;
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;
374 code = SSH2_FX_OK;
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)
395 break;
397 // Always send the total size after emitting mime-type...
398 totalSize(fileSize);
400 if (fd == -1)
401 data(mimeBuffer);
402 else
404 if ( (res.code=writeToFile(fd, mimeBuffer.data(), mimeBuffer.size())) != 0 )
405 return res;
408 processedSize(mimeBuffer.size());
409 mimeBuffer.resize(0);
410 foundMimetype = true;
413 else {
414 if (fd == -1)
415 data(buff);
416 else
418 if ( (res.code= writeToFile(fd, buff.data(), buff.size())) != 0 )
419 return res;
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.
430 if( wasKilled() ) {
431 res.text = i18n("An internal error occurred. Please retry the request again.");
432 res.code = ERR_UNKNOWN;
433 return res;
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?
442 res.size = offset;
443 sftpClose(handle);
444 processedSize (offset);
445 return res;
448 void sftpProtocol::get(const KUrl& url) {
449 kDebug(KIO_SFTP_DB) << "get(): " << url;
451 openConnection();
452 if( !mConnected )
453 return;
455 // Get resume offset
456 quint64 offset = config()->readEntry("resume",0);
457 if( offset > 0 ) {
458 canResume();
459 kDebug(KIO_SFTP_DB) << "get(): canResume(), offset = " << offset;
462 Status info = sftpGet(url, offset);
464 if (info.code != 0)
466 error(info.code, info.text);
467 return;
470 data(QByteArray());
471 kDebug(KIO_SFTP_DB) << "get(): emit finished()";
472 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 )
481 closeConnection();
483 mHost = h;
485 if( port > 0 )
486 mPort = port;
487 else {
488 struct servent *pse;
489 if( (pse = getservbyname("ssh", "tcp") ) == NULL )
490 mPort = 22;
491 else
492 mPort = ntohs(pse->s_port);
495 mUsername = user;
496 mPassword = pass;
498 if (user.isEmpty())
500 KUser u;
501 mUsername = u.loginName();
506 void sftpProtocol::openConnection() {
508 if(mConnected)
509 return;
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"));
519 return;
522 ////////////////////////////////////////////////////////////////////////////
523 // Setup AuthInfo for use with password caching and the
524 // password dialog box.
525 AuthInfo info;
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;
561 // opts.append(opt);
562 // opts.append(opt);
564 // setHost will set a port in any case
565 Q_ASSERT( mPort != -1 );
567 opt.opt = KSshProcess::SSH_PORT;
568 opt.num = mPort;
569 opts.append(opt);
571 opt.opt = KSshProcess::SSH_SUBSYSTEM;
572 opt.str = "sftp";
573 opts.append(opt);
575 opt.opt = KSshProcess::SSH_FORWARDX11;
576 opt.boolean = false;
577 opts.append(opt);
579 opt.opt = KSshProcess::SSH_FORWARDAGENT;
580 opt.boolean = false;
581 opts.append(opt);
583 opt.opt = KSshProcess::SSH_PROTOCOL;
584 opt.num = 2;
585 opts.append(opt);
587 opt.opt = KSshProcess::SSH_HOST;
588 opt.str = mHost;
589 opts.append(opt);
591 opt.opt = KSshProcess::SSH_ESCAPE_CHAR;
592 opt.num = -1; // don't use any escape character
593 opts.append(opt);
595 // set the username and password if we have them
596 if( !mUsername.isEmpty() ) {
597 opt.opt = KSshProcess::SSH_USERNAME;
598 opt.str = mUsername;
599 opts.append(opt);
600 usernameIt = opts.end()-1;
603 if( !mPassword.isEmpty() ) {
604 opt.opt = KSshProcess::SSH_PASSWD;
605 opt.str = mPassword;
606 opts.append(opt);
607 passwdIt = opts.end()-1;
610 ssh.setOptions(opts);
611 ssh.printArgs();
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;
621 bool dlgResult;
623 while( !(mConnected = ssh.connect()) ) {
624 err = ssh.error();
625 kDebug(KIO_SFTP_DB) << "openConnection(): "
626 "Got " << err << " from KSshProcess::connect()" << endl;
628 switch(err) {
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.");
637 else
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;
643 if( firstTime )
644 dlgResult = openPasswordDialog(info);
645 else
646 dlgResult = openPasswordDialog(info, i18n("Incorrect username or password"));
648 if( dlgResult ) {
649 if( info.username.isEmpty() || info.password.isEmpty() ) {
650 error(ERR_COULD_NOT_AUTHENTICATE,
651 i18n("Please enter a username and password"));
652 continue;
655 else {
656 // user canceled or dialog failed to open
657 error(ERR_USER_CANCELED, QString());
658 kDebug(KIO_SFTP_DB) << "openConnection(): user canceled, dlgResult = " << dlgResult;
659 closeConnection();
660 return;
663 firstTime = false;
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
670 // it again.
671 if( mUsername != info.username ) {
672 kDebug(KIO_SFTP_DB) << "openConnection(): Username changed from "
673 << mUsername << " to " << info.username << endl;
675 ssh.disconnect();
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;
685 opts.append(opt);
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;
693 opts.append(opt);
694 passwdIt = opts.end()-1;
697 (*usernameIt).str = info.username;
698 (*passwdIt).str = info.password;
699 ssh.setOptions(opts);
700 ssh.printArgs();
702 else { // just set the password
703 ssh.setPassword(info.password);
706 mUsername = info.username;
707 mPassword = info.password;
709 break;
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) ) {
715 closeConnection();
716 error(ERR_USER_CANCELED, QString());
717 return;
719 ssh.acceptHostKey(true);
720 break;
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) ) {
726 closeConnection();
727 error(ERR_USER_CANCELED, QString());
728 return;
730 ssh.acceptHostKey(true);
731 break;
733 case KSshProcess::ERR_AUTH_FAILED:
734 infoMessage(i18n("Authentication failed."));
735 error(ERR_COULD_NOT_LOGIN, i18n("Authentication failed."));
736 return;
738 case KSshProcess::ERR_AUTH_FAILED_NEW_KEY:
739 msg = ssh.errorMsg();
740 error(ERR_COULD_NOT_LOGIN, msg);
741 return;
743 case KSshProcess::ERR_AUTH_FAILED_DIFF_KEY:
744 msg = ssh.errorMsg();
745 error(ERR_COULD_NOT_LOGIN, msg);
746 return;
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()) {
753 caption += '\n';
754 caption += msg;
756 closeConnection();
757 error(ERR_COULD_NOT_LOGIN, caption);
758 return;
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:
766 default:
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()) {
772 caption += '\n';
773 caption += msg;
775 closeConnection();
776 error(ERR_UNKNOWN, caption);
777 return;
781 // catch all in case we did something wrong above
782 if( !mConnected ) {
783 error(ERR_INTERNAL, QString());
784 return;
787 // Now send init packet.
788 kDebug(KIO_SFTP_DB) << "openConnection(): Sending SSH2_FXP_INIT packet.";
789 QByteArray p;
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
795 putPacket(p);
796 getPacket(p);
798 QDataStream s(p);
799 quint32 version;
800 quint8 type;
801 s >> type;
802 kDebug(KIO_SFTP_DB) << "openConnection(): Got type " << type;
804 if( type == SSH2_FXP_VERSION ) {
805 s >> 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));
817 closeConnection();
818 return;
821 else {
822 error(ERR_UNKNOWN, i18n("Protocol error."));
823 closeConnection();
824 return;
827 // Login succeeded!
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);
838 mConnected = true;
839 connected();
841 mPassword.fill('x');
842 info.password.fill('x');
844 return;
847 #define _DEBUG kDebug(KIO_SFTP_DB)
849 void sftpProtocol::open(const KUrl &url, QIODevice::OpenMode mode)
851 _DEBUG << url.url() << endl;
852 openConnection();
853 if (!mConnected) {
854 error(KIO::ERR_CONNECTION_BROKEN, url.prettyUrl());
855 return;
858 int code;
859 sftpFileAttr attr(remoteEncoding());
861 if ((code = sftpStat(url, attr)) != SSH2_FX_OK) {
862 _DEBUG << "stat error" << endl;
863 processStatus(code, url.prettyUrl());
864 return;
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());
871 return;
873 if (attr.fileType() != S_IFREG) {
874 _DEBUG << "not a regular file" << endl;
875 error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.prettyUrl());
876 return;
879 KIO::filesize_t fileSize = attr.fileSize();
880 attr.clear();
882 quint32 pflags = 0;
884 if (mode & QIODevice::ReadOnly) {
885 if (mode & QIODevice::WriteOnly) {
886 pflags = SSH2_FXF_READ | SSH2_FXF_WRITE | SSH2_FXF_CREAT;
887 } else {
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());
904 return;
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){
912 QByteArray buffer;
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());
917 close();
918 return;
920 KMimeType::Ptr mime = KMimeType::findByNameAndContent(url.fileName(), buffer);
921 mimeType(mime->name());
924 openUrl = url;
925 openOffset = 0;
926 totalSize(fileSize);
927 position(0);
928 opened();
931 void sftpProtocol::read(KIO::filesize_t bytes)
933 _DEBUG << "read, offset = " << openOffset << ", bytes = " << bytes << endl;
934 QByteArray buffer;
935 int code = sftpRead(openHandle, openOffset, bytes, buffer);
936 if ((code == SSH2_FX_OK) || (code == SSH2_FX_EOF)) {
937 openOffset += buffer.size();
938 data(buffer);
939 } else {
940 processStatus(code, openUrl.prettyUrl());
941 close();
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());
952 } else {
953 processStatus(code, openUrl.prettyUrl());
954 close();
958 void sftpProtocol::seek(KIO::filesize_t offset)
960 _DEBUG << "seek, offset = " << offset << endl;
961 openOffset = offset;
962 position(offset);
965 void sftpProtocol::close()
967 sftpClose(openHandle);
968 _DEBUG << "emitting finished" << endl;
969 finished();
971 #undef _DEBUG
973 void sftpProtocol::closeConnection() {
974 kDebug(KIO_SFTP_DB) << "closeConnection()";
975 ssh.disconnect();
976 mConnected = false;
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());
985 return;
988 if (S_ISDIR (buff.st_mode)) {
989 error (ERR_IS_DIRECTORY, src.prettyUrl());
990 return;
993 int fd = KDE_open (file.data(), O_RDONLY);
994 if (fd == -1) {
995 error (ERR_CANNOT_OPEN_FOR_READING, src.prettyUrl());
996 return;
999 totalSize (buff.st_size);
1001 sftpPut (dest, permissions, (flags & ~KIO::Resume), fd);
1003 // Close the file descriptor...
1004 ::close( fd );
1007 void sftpProtocol::sftpPut( const KUrl& dest, int permissions, KIO::JobFlags flags, int fd ) {
1009 openConnection();
1010 if( !mConnected )
1011 return;
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());
1031 return;
1034 else {
1035 origExists = true;
1038 else if( code != SSH2_FX_NO_SUCH_FILE ) {
1039 processStatus(code, origUrl.prettyUrl());
1040 return;
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());
1047 return;
1050 // Stat file with part ext to see if it already exists...
1051 KUrl partUrl( origUrl );
1052 partUrl.setFileName( partUrl.fileName() + ".part" );
1054 quint64 offset = 0;
1055 bool partExists = false;
1056 bool markPartial = config()->readEntry("MarkPartial", true);
1058 if( markPartial ) {
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";
1065 partExists = true;
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
1070 // file is zero...
1071 if( origExists || offset == 0 )
1073 if( sftpRemove(partUrl, true) != SSH2_FX_OK ) {
1074 error(ERR_CANNOT_DELETE_PARTIAL, partUrl.prettyUrl());
1075 return;
1078 if( sftpRename(origUrl, partUrl) != SSH2_FX_OK ) {
1079 error(ERR_CANNOT_RENAME_ORIGINAL, origUrl.prettyUrl());
1080 return;
1083 offset = 0;
1085 else if( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) ) {
1086 if (fd != -1)
1087 flags |= (KDE_lseek(fd, offset, SEEK_SET) != -1) ? KIO::Resume : KIO::DefaultFlags;
1088 else
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());
1096 return;
1099 else {
1100 offset = 0;
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());
1106 return;
1109 else {
1110 processStatus(code, partUrl.prettyUrl());
1111 return;
1115 // Determine the url we will actually write to...
1116 KUrl writeUrl (markPartial ? partUrl:origUrl);
1118 quint32 pflags = 0;
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());
1129 QByteArray handle;
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);
1143 writeUrl = origUrl;
1146 if( code == SSH2_FX_FAILURE ) { // assume failure means file exists
1147 error(ERR_FILE_ALREADY_EXIST, writeUrl.prettyUrl());
1148 return;
1150 else {
1151 processStatus(code, writeUrl.prettyUrl());
1152 return;
1156 long nbytes;
1157 QByteArray buff;
1159 do {
1161 if( fd != -1 ) {
1162 buff.resize( 16*1024 );
1163 if ( (nbytes = ::read(fd, buff.data(), buff.size())) > -1 )
1164 buff.resize( nbytes );
1166 else {
1167 dataReq();
1168 nbytes = readData( buff );
1171 if( nbytes > 0 ) {
1172 if( (code = sftpWrite(handle, offset, buff)) != SSH2_FX_OK ) {
1173 error(ERR_COULD_NOT_WRITE, dest.prettyUrl());
1174 return;
1177 offset += nbytes;
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
1183 * up properly.
1185 if( wasKilled() ) {
1186 sftpClose(handle);
1187 closeConnection();
1188 error(ERR_UNKNOWN, i18n("An internal error occurred. Please try again."));
1189 return;
1193 } while( nbytes > 0 );
1195 if( nbytes < 0 ) {
1196 sftpClose(handle);
1198 if( markPartial ) {
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()) );
1211 return;
1214 if( (code = sftpClose(handle)) != SSH2_FX_OK ) {
1215 error(ERR_COULD_NOT_WRITE, writeUrl.prettyUrl());
1216 return;
1219 // If wrote to a partial file, then remove the part ext
1220 if( markPartial ) {
1221 if( sftpRename(partUrl, origUrl) != SSH2_FX_OK ) {
1222 error(ERR_CANNOT_RENAME_PARTIAL, origUrl.prettyUrl());
1223 return;
1227 finished();
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;
1240 openConnection();
1241 if( !mConnected )
1242 return;
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() ) {
1248 UDSEntry entry;
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 );
1256 // no size
1257 statEntry( entry );
1258 finished();
1259 return;
1262 int code;
1263 sftpFileAttr attr(remoteEncoding());
1264 if( (code = sftpStat(url, attr)) != SSH2_FX_OK ) {
1265 processStatus(code, url.prettyUrl());
1266 return;
1268 else {
1269 //kDebug() << "We sent and received stat packet ok";
1270 attr.setFilename(url.fileName());
1271 statEntry(attr.entry());
1274 finished();
1276 kDebug(KIO_SFTP_DB) << "stat: END";
1277 return;
1281 void sftpProtocol::mimetype ( const KUrl& url ){
1282 kDebug(KIO_SFTP_DB) << "mimetype(): " << url;
1284 openConnection();
1285 if( !mConnected )
1286 return;
1288 Status info = sftpGet(url, 0 /*offset*/, -1, true /*only emit mimetype*/);
1290 if (info.code != 0)
1292 error(info.code, info.text);
1293 return;
1296 finished();
1300 void sftpProtocol::listDir(const KUrl& url) {
1301 kDebug(KIO_SFTP_DB) << "listDir(): " << url;
1303 openConnection();
1304 if( !mConnected )
1305 return;
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);
1312 finished();
1313 return;
1317 int code;
1318 QByteArray handle;
1320 if( (code = sftpOpenDirectory(url, handle)) != SSH2_FX_OK ) {
1321 kError(KIO_SFTP_DB) << "listDir(): open directory failed" << endl;
1322 processStatus(code, url.prettyUrl());
1323 return;
1327 code = SSH2_FX_OK;
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());
1338 return;
1341 finished();
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:
1348 uint32 id
1349 string path
1350 ATTR attr
1352 void sftpProtocol::mkdir(const KUrl&url, int permissions){
1354 kDebug(KIO_SFTP_DB) << "mkdir() creating dir: " << url.path();
1356 openConnection();
1357 if( !mConnected )
1358 return;
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++;
1371 QByteArray p;
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;
1375 s << id;
1376 s.writeBytes(path.data(), len);
1377 s << attr;
1379 kDebug(KIO_SFTP_DB) << "mkdir(): packet size is " << p.size();
1381 putPacket(p);
1382 getPacket(p);
1384 quint8 type;
1385 QDataStream r(p);
1387 r >> type >> id;
1388 if( id != expectedId ) {
1389 kError(KIO_SFTP_DB) << "mkdir: sftp packet id mismatch" << endl;
1390 error(ERR_COULD_NOT_MKDIR, path);
1391 finished();
1392 return;
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);
1398 finished();
1399 return;
1402 int code;
1403 r >> code;
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() );
1413 return;
1416 error(ERR_COULD_NOT_MKDIR, path);
1419 finished();
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."));
1428 return;
1431 openConnection();
1432 if( !mConnected )
1433 return;
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() );
1448 else
1449 error( KIO::ERR_FILE_ALREADY_EXIST, dest.url() );
1450 return;
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);
1457 return;
1461 // Do the renaming...
1462 if( (code = sftpRename(src, dest)) != SSH2_FX_OK ) {
1463 processStatus(code);
1464 return;
1467 finished();
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."));
1477 return;
1480 openConnection();
1481 if( !mConnected )
1482 return;
1484 int code;
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 ) {
1490 failed = true;
1492 else {
1493 if( (code = sftpRemove(dest, !S_ISDIR(attr.permissions())) ) != SSH2_FX_OK ) {
1494 failed = true;
1496 else {
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 )
1501 failed = true;
1505 else if( code == SSH2_FX_FAILURE ) {
1506 error(ERR_FILE_ALREADY_EXIST, dest.prettyUrl());
1507 return;
1509 else
1510 failed = true;
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.
1515 if( failed )
1516 processStatus(code);
1518 finished();
1521 void sftpProtocol::chmod(const KUrl& url, int permissions){
1522 QString perms;
1523 perms.setNum(permissions, 8);
1524 kDebug(KIO_SFTP_DB) << "chmod(" << url << ", " << perms << ")";
1526 openConnection();
1527 if( !mConnected )
1528 return;
1530 sftpFileAttr attr(remoteEncoding());
1532 if (permissions != -1)
1533 attr.setPermissions(permissions);
1535 int code;
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());
1540 else
1541 processStatus(code, url.prettyUrl());
1543 finished();
1547 void sftpProtocol::del(const KUrl &url, bool isfile){
1548 kDebug(KIO_SFTP_DB) << "del(" << url << ", " << (isfile?"file":"dir") << ")";
1550 openConnection();
1551 if( !mConnected )
1552 return;
1554 int code;
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());
1559 finished();
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');
1572 #ifdef Q_WS_WIN
1573 ssize_t len;
1574 if(ssh.pty()->waitForReadyRead(2000)) {
1575 len = ssh.pty()->read(buf.data(), 4);
1577 #else
1578 // Get the message length...
1579 ssize_t len = atomicio(ssh.stdioFd(), buf.data(), 4, true /*read*/);
1580 #endif
1581 if( len == 0 || len == -1 ) {
1582 kDebug(KIO_SFTP_DB) << "getPacket(): read of packet length failed, ret = "
1583 << len << ", error =" << strerror(errno) << endl;
1584 closeConnection();
1585 error( ERR_CONNECTION_BROKEN, mHost);
1586 msg.resize(0);
1587 return false;
1590 int msgLen;
1591 QDataStream s(buf);
1592 s >> msgLen;
1594 //kDebug(KIO_SFTP_DB) << "getPacket(): Message size = " << msgLen;
1596 msg.resize(0);
1598 QBuffer b( &msg );
1599 b.open( QIODevice::WriteOnly );
1601 while( msgLen ) {
1602 #ifdef Q_WS_WIN
1603 len = ssh.pty()->read(buf.data(), qMin(buf.size(), msgLen));
1604 #else
1605 len = atomicio(ssh.stdioFd(), buf.data(), qMin(buf.size(), msgLen), true /*read*/);
1606 #endif
1608 if( len == 0 || len == -1) {
1609 QString errmsg;
1610 if (len == 0)
1611 errmsg = i18n("Connection closed");
1612 else
1613 errmsg = i18n("Could not read SFTP packet");
1614 kDebug(KIO_SFTP_DB) << "getPacket(): nothing to read, ret = " <<
1615 len << ", error =" << strerror(errno) << endl;
1616 closeConnection();
1617 error(ERR_CONNECTION_BROKEN, errmsg);
1618 b.close();
1619 return false;
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();
1627 msgLen -= len;
1630 b.close();
1632 return true;
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();
1638 int ret;
1639 #ifdef Q_WS_WIN
1640 ret = ssh.pty()->write(p.data(), p.size());
1641 #else
1642 ret = atomicio(ssh.stdioFd(), p.data(), p.size(), false /*write*/);
1643 #endif
1644 if( ret <= 0 ) {
1645 kDebug(KIO_SFTP_DB) << "putPacket(): write failed, ret =" << ret <<
1646 ", error = " << strerror(errno) << endl;
1647 return false;
1650 return true;
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++;
1667 QByteArray p;
1668 QDataStream s(&p, QIODevice::WriteOnly);
1669 s << quint32(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
1670 s << (quint8)SSH2_FXP_REALPATH;
1671 s << id;
1672 s.writeBytes(path.data(), len);
1674 putPacket(p);
1675 getPacket(p);
1677 quint8 type;
1678 QDataStream r(p);
1680 r >> type >> id;
1681 if( id != expectedId ) {
1682 kError(KIO_SFTP_DB) << "sftpRealPath: sftp packet id mismatch" << endl;
1683 return -1;
1686 if( type == SSH2_FXP_STATUS ) {
1687 quint32 code;
1688 r >> code;
1689 return code;
1692 if( type != SSH2_FXP_NAME ) {
1693 kError(KIO_SFTP_DB) << "sftpRealPath(): unexpected packet type of " << type << endl;
1694 return -1;
1697 quint32 count;
1698 r >> count;
1699 if( count != 1 ) {
1700 kError(KIO_SFTP_DB) << "sftpRealPath(): Bad number of file attributes for realpath command" << endl;
1701 return -1;
1704 QByteArray newPath;
1705 r >> newPath;
1707 newPath.truncate(newPath.size());
1708 if (newPath.isEmpty())
1709 newPath = "/";
1710 newUrl.setPath(newPath);
1712 return SSH2_FX_OK;
1715 sftpProtocol::Status sftpProtocol::doProcessStatus(quint8 code, const QString& message)
1717 Status res;
1718 res.code = 0;
1719 res.size = 0;
1720 res.text = message;
1722 switch(code)
1724 case SSH2_FX_OK:
1725 case SSH2_FX_EOF:
1726 res.text = i18n("End of file.");
1727 break;
1728 case SSH2_FX_NO_SUCH_FILE:
1729 res.code = ERR_DOES_NOT_EXIST;
1730 break;
1731 case SSH2_FX_PERMISSION_DENIED:
1732 res.code = ERR_ACCESS_DENIED;
1733 break;
1734 case SSH2_FX_FAILURE:
1735 res.text = i18n("SFTP command failed for an unknown reason.");
1736 res.code = ERR_UNKNOWN;
1737 break;
1738 case SSH2_FX_BAD_MESSAGE:
1739 res.text = i18n("The SFTP server received a bad message.");
1740 res.code = ERR_UNKNOWN;
1741 break;
1742 case SSH2_FX_OP_UNSUPPORTED:
1743 res.text = i18n("You attempted an operation unsupported by the SFTP server.");
1744 res.code = ERR_UNKNOWN;
1745 break;
1746 default:
1747 res.text = i18n("Error code: %1", code);
1748 res.code = ERR_UNKNOWN;
1751 return res;
1754 /** Process SSH_FXP_STATUS packets. */
1755 void sftpProtocol::processStatus(quint8 code, const QString& message){
1756 Status st = doProcessStatus( code, message );
1757 if( st.code != 0 ){
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++;
1773 QByteArray p;
1774 QDataStream s(&p, QIODevice::WriteOnly);
1775 s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
1776 s << (quint8)SSH2_FXP_OPENDIR;
1777 s << (quint32)id;
1778 s.writeBytes(path.data(), len);
1780 putPacket(p);
1781 getPacket(p);
1783 QDataStream r(p);
1784 quint8 type;
1786 r >> type >> id;
1787 if( id != expectedId ) {
1788 kError(KIO_SFTP_DB) << "sftpOpenDirectory: sftp packet id mismatch: " <<
1789 "expected " << expectedId << ", got " << id << endl;
1790 return -1;
1793 if( type == SSH2_FXP_STATUS ) {
1794 quint32 errCode;
1795 r >> errCode;
1796 return errCode;
1799 if( type != SSH2_FXP_HANDLE ) {
1800 kError(KIO_SFTP_DB) << "sftpOpenDirectory: unexpected message type of " << type << endl;
1801 return -1;
1804 r >> handle;
1805 if( handle.size() > 256 ) {
1806 kError(KIO_SFTP_DB) << "sftpOpenDirectory: handle exceeds max length" << endl;
1807 return -1;
1810 kDebug(KIO_SFTP_DB) << "sftpOpenDirectory: handle (" << handle.size() << "): [" << handle << "]";
1811 return SSH2_FX_OK;
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++;
1822 QByteArray p;
1823 QDataStream s(&p, QIODevice::WriteOnly);
1824 s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + handle.size());
1825 s << (quint8)SSH2_FXP_CLOSE;
1826 s << (quint32)id;
1827 s << handle;
1829 putPacket(p);
1830 getPacket(p);
1832 QDataStream r(p);
1833 quint8 type;
1835 r >> type >> id;
1836 if( id != expectedId ) {
1837 kError(KIO_SFTP_DB) << "sftpClose: sftp packet id mismatch" << endl;
1838 return -1;
1841 if( type != SSH2_FXP_STATUS ) {
1842 kError(KIO_SFTP_DB) << "sftpClose: unexpected message type of " << type << endl;
1843 return -1;
1846 quint32 code;
1847 r >> code;
1848 if( code != SSH2_FX_OK ) {
1849 kError(KIO_SFTP_DB) << "sftpClose: close failed with err code " << code << endl;
1852 return code;
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++;
1866 QByteArray p;
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;
1870 s << (quint32)id;
1871 s.writeBytes(path.data(), len);
1872 s << attr;
1874 putPacket(p);
1875 getPacket(p);
1877 QDataStream r(p);
1878 quint8 type;
1880 r >> type >> id;
1881 if( id != expectedId ) {
1882 kError(KIO_SFTP_DB) << "sftpSetStat(): sftp packet id mismatch" << endl;
1883 return -1;
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;
1889 return -1;
1892 quint32 code;
1893 r >> code;
1894 if( code != SSH2_FX_OK ) {
1895 kError(KIO_SFTP_DB) << "sftpSetStat(): set stat failed with err code " << code << endl;
1898 return code;
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++;
1912 QByteArray p;
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);
1916 s << (quint32)id;
1917 s.writeBytes(path.data(), len);
1919 putPacket(p);
1920 getPacket(p);
1922 QDataStream r(p);
1923 quint8 type;
1925 r >> type >> id;
1926 if( id != expectedId ) {
1927 kError(KIO_SFTP_DB) << "del(): sftp packet id mismatch" << endl;
1928 return -1;
1931 if( type != SSH2_FXP_STATUS ) {
1932 kError(KIO_SFTP_DB) << "del(): unexpected message type of " << type << endl;
1933 return -1;
1936 quint32 code;
1937 r >> code;
1938 if( code != SSH2_FX_OK ) {
1939 kError(KIO_SFTP_DB) << "del(): del failed with err code " << code << endl;
1942 return code;
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++;
1959 QByteArray p;
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;
1965 s << (quint32)id;
1966 s.writeBytes(srcPath.data(), slen);
1967 s.writeBytes(destPath.data(), dlen);
1969 putPacket(p);
1970 getPacket(p);
1972 QDataStream r(p);
1973 quint8 type;
1975 r >> type >> id;
1976 if( id != expectedId ) {
1977 kError(KIO_SFTP_DB) << "sftpRename(): sftp packet id mismatch" << endl;
1978 return -1;
1981 if( type != SSH2_FXP_STATUS ) {
1982 kError(KIO_SFTP_DB) << "sftpRename(): unexpected message type of " << type << endl;
1983 return -1;
1986 int code;
1987 r >> code;
1988 if( code != SSH2_FX_OK ) {
1989 kError(KIO_SFTP_DB) << "sftpRename(): rename failed with err code " << code << endl;
1992 return code;
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;
2000 quint8 type;
2002 sftpFileAttr attr (remoteEncoding());
2003 attr.setDirAttrsFlag(true);
2005 QByteArray p;
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;
2010 s << (quint32)id;
2011 s << handle;
2013 putPacket(p);
2014 getPacket(p);
2016 QDataStream r(p);
2017 r >> type >> id;
2019 if( id != expectedId ) {
2020 kError(KIO_SFTP_DB) << "sftpReadDir(): sftp packet id mismatch" << endl;
2021 return -1;
2024 int code;
2025 if( type == SSH2_FXP_STATUS ) {
2026 r >> code;
2027 return code;
2030 if( type != SSH2_FXP_NAME ) {
2031 kError(KIO_SFTP_DB) << "kio_sftpProtocol::sftpReadDir(): Unexpected message" << endl;
2032 return -1;
2035 r >> count;
2036 kDebug(KIO_SFTP_DB) << "sftpReadDir(): got " << count << " entries";
2038 while(count--) {
2039 r >> attr;
2041 if( S_ISLNK(attr.permissions()) ) {
2042 KUrl myurl ( url );
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);
2058 return SSH2_FX_OK;
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++;
2074 QByteArray p;
2075 QDataStream s(&p, QIODevice::WriteOnly);
2076 s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
2077 s << (quint8)SSH2_FXP_READLINK;
2078 s << id;
2079 s.writeBytes(path.data(), len);
2082 putPacket(p);
2083 getPacket(p);
2085 quint8 type;
2086 QDataStream r(p);
2088 r >> type >> id;
2089 if( id != expectedId ) {
2090 kError(KIO_SFTP_DB) << "sftpReadLink(): sftp packet id mismatch" << endl;
2091 return -1;
2094 if( type == SSH2_FXP_STATUS ) {
2095 quint32 code;
2096 r >> code;
2097 kDebug(KIO_SFTP_DB) << "sftpReadLink(): read link failed with code " << code;
2098 return code;
2101 if( type != SSH2_FXP_NAME ) {
2102 kError(KIO_SFTP_DB) << "sftpReadLink(): unexpected packet type of " << type << endl;
2103 return -1;
2106 quint32 count;
2107 r >> count;
2108 if( count != 1 ) {
2109 kError(KIO_SFTP_DB) << "sftpReadLink(): Bad number of file attributes for realpath command" << endl;
2110 return -1;
2113 QByteArray linkAddress;
2114 r >> linkAddress;
2116 linkAddress.truncate(linkAddress.size());
2117 kDebug(KIO_SFTP_DB) << "sftpReadLink(): Link address: " << linkAddress;
2119 target = remoteEncoding()->decode(linkAddress);
2121 return SSH2_FX_OK;
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++;
2136 QByteArray p;
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;
2142 s << (quint32)id;
2143 s.writeBytes(target.data(), tlen);
2144 s.writeBytes(destPath.data(), dlen);
2146 putPacket(p);
2147 getPacket(p);
2149 QDataStream r(p);
2150 quint8 type;
2152 r >> type >> id;
2153 if( id != expectedId ) {
2154 kError(KIO_SFTP_DB) << "sftpSymLink(): sftp packet id mismatch" << endl;
2155 return -1;
2158 if( type != SSH2_FXP_STATUS ) {
2159 kError(KIO_SFTP_DB) << "sftpSymLink(): unexpected message type of " << type << endl;
2160 return -1;
2163 quint32 code;
2164 r >> code;
2165 if( code != SSH2_FX_OK ) {
2166 kError(KIO_SFTP_DB) << "sftpSymLink(): rename failed with err code " << code << endl;
2169 return code;
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++;
2183 QByteArray p;
2184 QDataStream s(&p, QIODevice::WriteOnly);
2185 s << (quint32)(1 /*type*/ + 4 /*id*/ + 4 /*str length*/ + len);
2186 s << (quint8)SSH2_FXP_LSTAT;
2187 s << (quint32)id;
2188 s.writeBytes(path.data(), len);
2190 putPacket(p);
2191 getPacket(p);
2193 QDataStream r(p);
2194 quint8 type;
2196 r >> type >> id;
2197 if( id != expectedId ) {
2198 kError(KIO_SFTP_DB) << "sftpStat(): sftp packet id mismatch" << endl;
2199 return -1;
2202 if( type == SSH2_FXP_STATUS ) {
2203 quint32 errCode;
2204 r >> errCode;
2205 kError(KIO_SFTP_DB) << "sftpStat(): stat failed with code " << errCode << endl;
2206 return errCode;
2209 if( type != SSH2_FXP_ATTRS ) {
2210 kError(KIO_SFTP_DB) << "sftpStat(): unexpected message type of " << type << endl;
2211 return -1;
2214 r >> attr;
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) ) {
2222 QString target;
2223 int code = sftpReadLink( url, target );
2225 if ( code != SSH2_FX_OK ) {
2226 kError(KIO_SFTP_DB) << "sftpStat(): Unable to stat symlink destination" << endl;
2227 return -1;
2230 kDebug(KIO_SFTP_DB) << "sftpStat(): Resource is a symlink to -> " << target;
2232 KUrl dest( url );
2233 if( target[0] == '/' )
2234 dest.setPath(target);
2235 else
2236 dest.setFileName(target);
2238 dest.cleanPath();
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());
2248 else
2249 attr.setLinkType(attr2.linkType());
2251 attr.setLinkDestination(target);
2253 kDebug(KIO_SFTP_DB) << "sftpStat(): File type: " << attr.fileType();
2257 return SSH2_FX_OK;
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++;
2271 QByteArray p;
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;
2277 s << (quint32)id;
2278 s.writeBytes(path.data(), len);
2279 s << pflags;
2280 s << attr;
2282 putPacket(p);
2283 getPacket(p);
2285 QDataStream r(p);
2286 quint8 type;
2288 r >> type >> id;
2289 if( id != expectedId ) {
2290 kError(KIO_SFTP_DB) << "sftpOpen(): sftp packet id mismatch" << endl;
2291 return -1;
2294 if( type == SSH2_FXP_STATUS ) {
2295 quint32 errCode;
2296 r >> errCode;
2297 return errCode;
2300 if( type != SSH2_FXP_HANDLE ) {
2301 kError(KIO_SFTP_DB) << "sftpOpen(): unexpected message type of " << type << endl;
2302 return -1;
2305 r >> handle;
2306 if( handle.size() > 256 ) {
2307 kError(KIO_SFTP_DB) << "sftpOpen(): handle exceeds max length" << endl;
2308 return -1;
2311 kDebug(KIO_SFTP_DB) << "sftpOpen(): handle (" << handle.size() << "): [" << handle << "]";
2312 return SSH2_FX_OK;
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 << ")";
2319 QByteArray p;
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;
2328 s << (quint32)id;
2329 s << handle;
2330 s << offset; // we don't have a convienient 64 bit int so set upper int to zero
2331 s << len;
2333 putPacket(p);
2334 getPacket(p);
2336 QDataStream r(p);
2337 quint8 type;
2339 r >> type >> id;
2340 if( id != expectedId ) {
2341 kError(KIO_SFTP_DB) << "sftpRead: sftp packet id mismatch" << endl;
2342 return -1;
2345 if( type == SSH2_FXP_STATUS ) {
2346 quint32 errCode;
2347 r >> errCode;
2348 kError(KIO_SFTP_DB) << "sftpRead: read failed with code " << errCode << endl;
2349 return errCode;
2352 if( type != SSH2_FXP_DATA ) {
2353 kError(KIO_SFTP_DB) << "sftpRead: unexpected message type of " << type << endl;
2354 return -1;
2357 r >> data;
2359 return SSH2_FX_OK;
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;
2366 QByteArray p;
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() +
2373 8 /*offset*/ +
2374 4 /* data size */ + data.size());
2375 s << (quint8)SSH2_FXP_WRITE;
2376 s << (quint32)id;
2377 s << handle;
2378 s << offset; // we don't have a convienient 64 bit int so set upper int to zero
2379 s << data;
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 << "]";
2386 putPacket(p);
2387 getPacket(p);
2389 // kDebug(KIO_SFTP_DB) << "sftpWrite(): received packet [" << p << "]";
2391 QDataStream r(p);
2392 quint8 type;
2394 r >> type >> id;
2395 if( id != expectedId ) {
2396 kError(KIO_SFTP_DB) << "sftpWrite(): sftp packet id mismatch, got "
2397 << id << ", expected " << expectedId << endl;
2398 return -1;
2401 if( type != SSH2_FXP_STATUS ) {
2402 kError(KIO_SFTP_DB) << "sftpWrite(): unexpected message type of " << type << endl;
2403 return -1;
2406 quint32 code;
2407 r >> code;
2408 return code;