1 /***************************************************************************
2 fish.cpp - a FISH kioslave
4 begin : Thu Oct 4 17:09:14 CEST 2001
5 copyright : (C) 2001-2003 by Jörg Walter
6 email : jwalt-kde@garni.ch
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, version 2 of the License *
15 ***************************************************************************/
18 This code contains fragments and ideas from the ftp kioslave
19 done by David Faure <faure@kde.org>.
21 Structure is a bit complicated, since I made the mistake to use
22 KProcess... now there is a lightweight homebrew async IO system
23 inside, but if signals/slots become available for ioslaves, switching
24 back to KProcess should be easy.
29 #include <config-runtime.h>
30 #include "config-fish.h"
49 #include <sys/socket.h>
50 #include <netinet/in.h>
52 #include <sys/types.h>
58 #ifdef HAVE_SYS_IOCTL_H
59 #include <sys/ioctl.h>
71 #include <kmessagebox.h>
72 #include <kcomponentdata.h>
74 #include <kstandarddirs.h>
76 #include <kremoteencoding.h>
81 #include <kmimetype.h>
84 #include <sys/resource.h>
90 #define myDebug(x) kDebug(7127) << __LINE__ << ": " x
91 #define connected() do{myDebug( << "_______ emitting connected()" << endl); connected();}while(0)
92 #define dataReq() do{myDebug( << "_______ emitting dataReq()" << endl); dataReq();}while(0)
93 #define needSubURLData() do{myDebug( << "_______ emitting needSubURLData()" << endl); needSubURLData();}while(0)
94 #define slaveStatus(x,y) do{myDebug( << "_______ emitting slaveStatus(" << x << ", " << y << ")" << endl); slaveStatus(x,y);}while(0)
95 #define statEntry(x) do{myDebug( << "_______ emitting statEntry("<<x.count()<<")" << endl); statEntry(x);}while(0)
96 #define listEntries(x) do{myDebug( << "_______ emitting listEntries(...)" << endl); listEntries(x);}while(0)
97 #define canResume(x) do{myDebug( << "_______ emitting canResume("<<(int)x<<")" << endl); canResume(x);}while(0)
98 #define totalSize(x) do{myDebug( << "_______ emitting totalSize("<<(int)x<<")" << endl); totalSize(x);}while(0)
99 #define processedSize(x) do{myDebug( << "_______ emitting processedSize("<<x<<")" << endl); processedSize(x);}while(0)
100 #define speed(x) do{myDebug( << "_______ emitting speed("<<(int)x<<")" << endl); speed(x);}while(0)
101 #define redirection(x) do{myDebug( << "_______ emitting redirection("<<x<<")" << endl); redirection(x);}while(0)
102 #define errorPage() do{myDebug( << "_______ emitting errorPage()" << endl); errorPage();}while(0)
103 #define sendmimeType(x) do{myDebug( << "_______ emitting mimeType("<<x<<")" << endl); mimeType(x);}while(0)
104 #define warning(x) do{myDebug( << "_______ emitting warning("<<x<<")" << endl); warning(x);}while(0)
105 #define infoMessage(x) do{myDebug( << "_______ emitting infoMessage("<<x<<")" << endl); infoMessage(x);}while(0)
108 #define sendmimeType(x) mimeType(x)
112 #define ENDLINE "\r\n"
117 static char *sshPath
= NULL
;
118 static char *suPath
= NULL
;
119 // disabled: currently not needed. Didn't work reliably.
120 // static int isOpenSSH = 0;
122 /** the SSH process used to communicate with the remote end */
124 static pid_t childPid
;
126 static KProcess
*childPid
= 0;
129 #define E(x) ((const char*)remoteEncoding()->encode(x).data())
134 int KDE_EXPORT
kdemain( int argc
, char **argv
)
136 KComponentData
componentData("fish", "kio_fish");
138 myDebug( << "*** Starting fish " << endl
);
140 myDebug( << "Usage: fish protocol domain-socket1 domain-socket2" << endl
);
144 setenv("TZ", "UTC", true);
146 fishProtocol
slave(argv
[2], argv
[3]);
147 slave
.dispatchLoop();
149 myDebug( << "*** fish Done" << endl
);
155 const struct fishProtocol::fish_info
fishProtocol::fishInfo
[] = {
157 ("echo; /bin/sh -c start_fish_server > /dev/null 2>/dev/null; perl .fishsrv.pl " CHECKSUM
" 2>/dev/null; perl -e '$|=1; print \"### 100 transfer fish server\\n\"; while(<STDIN>) { last if /^__END__/; $code.=$_; } exit(eval($code));' 2>/dev/null;"),
159 { ("VER 0.0.3 copy append lscount lslinks lsmime exec stat"), 0,
160 ("echo 'VER 0.0.3 copy append lscount lslinks lsmime exec stat'"),
166 ("echo `ls -Lla %1 2> /dev/null | grep '^[-dsplcb]' | wc -l`; ls -Lla %1 2>/dev/null | grep '^[-dspl]' | ( while read -r p x u g s m d y n; do file -b -i $n 2>/dev/null | sed -e '\\,^[^/]*$,d;s/^/M/;s,/.*[ \t],/,'; FILE=%1; if [ -e %1\"/$n\" ]; then FILE=%1\"/$n\"; fi; if [ -L \"$FILE\" ]; then echo \":$n\"; ls -lad \"$FILE\" | sed -e 's/.* -> /L/'; else echo \":$n\" | sed -e 's/ -> /\\\nL/'; fi; echo \"P$p $u.$g\nS$s\nd$m $d $y\n\"; done; );"
167 "ls -Lla %1 2>/dev/null | grep '^[cb]' | ( while read -r p x u g a i m d y n; do echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"; done; )"),
170 ("echo `ls -dLla %1 2> /dev/null | grep '^[-dsplcb]' | wc -l`; ls -dLla %1 2>/dev/null | grep '^[-dspl]' | ( while read -r p x u g s m d y n; do file -b -i $n 2>/dev/null | sed -e '\\,^[^/]*$,d;s/^/M/;s,/.*[ \t],/,'; FILE=%1; if [ -e %1\"/$n\" ]; then FILE=%1\"/$n\"; fi; if [ -L \"$FILE\" ]; then echo \":$n\"; ls -lad \"$FILE\" | sed -e 's/.* -> /L/'; else echo \":$n\" | sed -e 's/ -> /\\\nL/'; fi; echo \"P$p $u.$g\nS$s\nd$m $d $y\n\"; done; );"
171 "ls -dLla %1 2>/dev/null | grep '^[cb]' | ( while read -r p x u g a i m d y n; do echo \"P$p $u.$g\nE$a$i\nd$m $d $y\n:$n\n\"; done; )"),
174 ("ls -l %1 2>&1 | ( read -r a b c d x e; echo $x ) 2>&1; echo '### 001'; cat %1"),
177 ("> %2; echo '### 001'; ( [ \"`expr %1 / 4096`\" -gt 0 ] && dd bs=4096 count=`expr %1 / 4096` 2>/dev/null;"
178 "[ \"`expr %1 % 4096`\" -gt 0 ] && dd bs=`expr %1 % 4096` count=1 2>/dev/null; ) | ( cat > %2 || echo Error $?; cat > /dev/null )"),
211 ("echo '### 100';cat %3 /dev/zero | ( [ \"`expr %1 / 4096`\" -gt 0 ] && dd bs=4096 count=`expr %1 / 4096` >/dev/null;"
212 "[ \"`expr %1 % 4096`\" -gt 0 ] && dd bs=`expr %1 % 4096` count=1 >/dev/null;"
213 "dd bs=%2 count=1; ) 2>/dev/null;"),
215 // Yes, this is "ibs=1", since dd "count" is input blocks.
216 // On network connections, read() may not fill the buffer
217 // completely (no more data immediately available), but dd
218 // does ignore that fact by design. Sorry, writes are slow.
219 // OTOH, WRITE is not used by the current ioslave methods,
222 (">> %3; echo '### 001'; ( [ %2 -gt 0 ] && dd ibs=1 obs=%2 count=%2 2>/dev/null ) | "
223 "( dd ibs=32768 obs=%1 seek=1 of=%3 2>/dev/null || echo Error $?; cat >/dev/null; )"),
226 ("if [ -L %1 ]; then if cp -pdf %1 %2 2>/dev/null; then :; else LINK=\"`readlink %1`\"; ln -sf $LINK %2; fi; else cp -pf %1 %2; fi"),
229 (">> %2; echo '### 001'; ( [ %1 -gt 0 ] && dd ibs=1 obs=%1 count=%1 2> /dev/null; ) | ( cat >> %2 || echo Error $?; cat >/dev/null; )"),
232 ("UMASK=`umask`; umask 077; touch %2; umask $UMASK; eval %1 < /dev/null > %2 2>&1; echo \"###RESULT: $?\" >> %2"),
236 fishProtocol::fishProtocol(const QByteArray
&pool_socket
, const QByteArray
&app_socket
)
237 : SlaveBase("fish", pool_socket
, app_socket
), mimeBuffer(1024, '\0'),
240 myDebug( << "fishProtocol::fishProtocol()" << endl
);
241 if (sshPath
== NULL
) {
242 // disabled: currently not needed. Didn't work reliably.
243 // isOpenSSH = !system("ssh -V 2>&1 | grep OpenSSH > /dev/null");
245 sshPath
= strdup(QFile::encodeName(KStandardDirs::findExe("plink")));
247 sshPath
= strdup(QFile::encodeName(KStandardDirs::findExe("ssh")));
250 if (suPath
== NULL
) {
251 suPath
= strdup(QFile::encodeName(KStandardDirs::findExe("su")));
264 connectionAuth
.keepPassword
= true;
265 connectionAuth
.url
.setProtocol("fish");
274 isStat
= false; // FIXME: just a workaround for konq deficiencies
275 redirectUser
= ""; // FIXME: just a workaround for konq deficiencies
276 redirectPass
= ""; // FIXME: just a workaround for konq deficiencies
277 fishCodeLen
= strlen(fishCode
);
279 /* ---------------------------------------------------------------------------------- */
282 fishProtocol::~fishProtocol()
284 myDebug( << "fishProtocol::~fishProtocol()" << endl
);
285 shutdownConnection(true);
288 /* --------------------------------------------------------------------------- */
291 Connects to a server and logs us in via SSH. Then starts FISH protocol.
293 void fishProtocol::openConnection() {
294 if (childPid
) return;
296 if (connectionHost
.isEmpty())
298 error( KIO::ERR_UNKNOWN_HOST
, QString() );
302 infoMessage(i18n("Connecting..."));
304 myDebug( << "connecting to: " << connectionUser
<< "@" << connectionHost
<< ":" << connectionPort
<< endl
);
305 sendCommand(FISH_FISH
);
306 sendCommand(FISH_VER
);
307 if (connectionStart()) {
308 error(ERR_COULD_NOT_CONNECT
,connectionHost
);
309 shutdownConnection();
312 myDebug( << "subprocess is running" << endl
);
317 static int open_pty_pair(int fd
[2])
319 #if defined(HAVE_TERMIOS_H) && defined(HAVE_GRANTPT) && !defined(HAVE_OPENPTY)
320 /** with kind regards to The GNU C Library
321 Reference Manual for Version 2.2.x of the GNU C Library */
325 memset(&ti
,0,sizeof(ti
));
327 ti
.c_cflag
= CLOCAL
|CREAD
|CS8
;
333 master
= open("/dev/ptmx", O_RDWR
);
335 if (master
< 0) return 0;
337 if (grantpt(master
) < 0 || unlockpt(master
) < 0) goto close_master
;
339 name
= ptsname(master
);
340 if (name
== NULL
) goto close_master
;
342 slave
= open(name
, O_RDWR
);
343 if (slave
== -1) goto close_master
;
345 #if (defined(HAVE_ISASTREAM) || defined(isastream)) && defined(I_PUSH)
346 if (isastream(slave
) &&
347 (ioctl(slave
, I_PUSH
, "ptem") < 0 ||
348 ioctl(slave
, I_PUSH
, "ldterm") < 0))
352 tcsetattr(slave
, TCSANOW
, &ti
);
357 #if (defined(HAVE_ISASTREAM) || defined(isastream)) && defined(I_PUSH)
368 memset(&ti
,0,sizeof(ti
));
370 ti
.c_cflag
= CLOCAL
|CREAD
|CS8
;
373 return openpty(fd
,fd
+1,NULL
,&ti
,NULL
);
376 #warning "No tty support available. Password dialog won't work."
378 return socketpair(PF_UNIX
,SOCK_STREAM
,0,fd
);
384 creates the subprocess
386 bool fishProtocol::connectionStart() {
392 rc
= open_pty_pair(fd
);
394 myDebug( << "socketpair failed, error: " << strerror(errno
) << endl
);
399 if (!requestNetwork()) return true;
400 myDebug( << "Exec: " << (local
? suPath
: sshPath
) << " Port: " << connectionPort
<< " User: " << connectionUser
<< endl
);
402 childPid
= new KProcess();
403 childPid
->setOutputChannelMode(KProcess::MergedChannels
);
404 QStringList common_args
;
405 common_args
<< "-l" << connectionUser
.toLatin1().constData() << "-x" << connectionHost
.toLatin1().constData();
406 common_args
<< "echo;echo FISH:;exec /bin/sh -c \"if env true 2>/dev/null; then env PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; else PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; fi\"";
408 childPid
->setProgram(sshPath
, common_args
);
413 while (!isLoggedIn
) {
415 rc
= childPid
->write(outBuf
);
421 myDebug( << "write failed, rc: " << rc
);
426 if (childPid
->waitForReadyRead(1000)) {
427 QByteArray buf2
= childPid
->readAll();
430 int noff
= establishConnection(buf
);
431 if (noff
< 0) return false;
432 if (noff
> 0) buf
= buf
.mid(/*offset+*/noff
);
438 if (childPid
== -1) {
439 myDebug( << "fork failed, error: " << strerror(errno
) << endl
);
447 // taken from konsole, see TEPty.C for details
448 // note: if we're running on socket pairs,
449 // this will fail, but thats what we expect
451 for (int sig
= 1; sig
< NSIG
; sig
++) signal(sig
,SIG_DFL
);
454 getrlimit(RLIMIT_NOFILE
, &rlp
);
455 for (int i
= 0; i
< (int)rlp
.rlim_cur
; i
++)
456 if (i
!= fd
[1]) ::close(i
);
461 if (fd
[1] > 2) ::close(fd
[1]);
465 #if defined(TIOCSCTTY)
466 ioctl(0, TIOCSCTTY
, 0);
470 #if defined( _AIX) || defined( __hpux)
473 ioctl(0, TIOCSPGRP
, (char *)&pgrp
);
476 const char *dev
= ttyname(0);
478 if (dev
) ::close(::open(dev
, O_WRONLY
, 0));
482 execl(suPath
, "su", "-", connectionUser
.toLatin1().constData(), "-c", "cd ~;echo FISH:;exec /bin/sh -c \"if env true 2>/dev/null; then env PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; else PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; fi\"", (void *)0);
484 #define common_args "-l", connectionUser.toLatin1().constData(), "-x", "-e", "none", \
485 "-q", connectionHost.toLatin1().constData(), \
486 "echo FISH:;exec /bin/sh -c \"if env true 2>/dev/null; then env PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; else PS1= PS2= TZ=UTC LANG=C LC_ALL=C LOCALE=C /bin/sh; fi\"", (void *)0
487 // disabled: leave compression up to the client.
488 // (isOpenSSH?"-C":"+C"),
491 execl(sshPath
, "ssh", "-p", qPrintable(QString::number(connectionPort
)), common_args
);
493 execl(sshPath
, "ssh", common_args
);
496 myDebug( << "could not exec! " << strerror(errno
) << endl
);
500 rc
= fcntl(fd
[0],F_GETFL
,&flags
);
501 rc
= fcntl(fd
[0],F_SETFL
,flags
|O_NONBLOCK
);
509 while (!isLoggedIn
) {
510 FD_SET(childFd
,&rfds
);
512 if (outBufPos
>= 0) FD_SET(childFd
,&wfds
);
513 struct timeval timeout
;
515 timeout
.tv_usec
= 1000;
516 rc
= select(childFd
+1, &rfds
, &wfds
, NULL
, &timeout
);
520 myDebug( << "select failed, rc: " << rc
<< ", error: " << strerror(errno
) << endl
);
523 if (FD_ISSET(childFd
,&wfds
) && outBufPos
>= 0) {
524 if (outBuf
) rc
= ::write(childFd
,outBuf
+outBufPos
,outBufLen
-outBufPos
);
526 if (rc
>= 0) outBufPos
+= rc
;
530 myDebug( << "write failed, rc: " << rc
<< ", error: " << strerror(errno
) << endl
);
534 if (outBufPos
>= outBufLen
) {
540 if (FD_ISSET(childFd
,&rfds
)) {
541 rc
= ::read(childFd
,buf
+offset
,32768-offset
);
543 int noff
= establishConnection(buf
,rc
+offset
);
544 if (noff
< 0) return false;
545 if (noff
> 0) memmove(buf
,buf
+offset
+rc
-noff
,noff
);
550 myDebug( << "read failed, rc: " << rc
<< ", error: " << strerror(errno
) << endl
);
560 writes one chunk of data to stdin of child process
563 void fishProtocol::writeChild(const char *buf
, KIO::fileoffset_t len
) {
564 if (outBufPos
>= 0 && outBuf
) {
566 void fishProtocol::writeChild(const QByteArray
&buf
, KIO::fileoffset_t len
) {
567 if (outBufPos
>= 0 && outBuf
.size()) {
571 debug
.setLatin1(outBuf
,outBufLen
);
572 if (len
> 0) myDebug( << "write request while old one is pending, throwing away input (" << outBufLen
<< "," << outBufPos
<< "," << debug
.left(10) << "...)" << endl
);
582 manages initial communication setup including password queries
585 int fishProtocol::establishConnection(char *buffer
, KIO::fileoffset_t len
) {
586 QString buf
= QString::fromLatin1(buffer
,len
);
588 int fishProtocol::establishConnection(const QByteArray
&buffer
) {
589 QString buf
= buffer
;
592 // Strip trailing whitespace
593 while (buf
.length() && (buf
[buf
.length()-1] == ' '))
594 buf
.truncate(buf
.length()-1);
596 myDebug( << "establishing: got " << buf
<< endl
);
597 while (childPid
&& ((pos
= buf
.indexOf('\n')) >= 0 ||
598 buf
.endsWith(':') || buf
.endsWith('?'))) {
600 QString str
= buf
.left(pos
);
604 if (str
== "FISH:\n") {
606 infoMessage(i18n("Initiating protocol..."));
607 if (!connectionAuth
.password
.isEmpty()) {
608 connectionAuth
.password
= connectionAuth
.password
.left(connectionAuth
.password
.length()-1);
609 cacheAuthentication(connectionAuth
);
613 } else if (!str
.isEmpty()) {
615 } else if (buf
.endsWith(':')) {
616 if (!redirectUser
.isEmpty() && connectionUser
!= redirectUser
) {
618 dest
.setUser(redirectUser
);
619 dest
.setPass(redirectPass
);
622 commandCodes
.clear();
627 } else if (!connectionPassword
.isEmpty()) {
628 myDebug( << "sending cpass" << endl
);
629 connectionAuth
.password
= connectionPassword
+ENDLINE
;
630 connectionPassword
.clear();
631 // su does not like receiving a password directly after sending
632 // the password prompt so we wait a while.
635 writeChild(connectionAuth
.password
.toLatin1(),connectionAuth
.password
.length());
637 myDebug( << "sending mpass" << endl
);
638 connectionAuth
.prompt
= thisFn
+buf
;
640 connectionAuth
.caption
= i18n("Local Login");
642 connectionAuth
.caption
= i18n("SSH Authorization");
643 if ((!firstLogin
|| !checkCachedAuthentication(connectionAuth
))) {
644 connectionAuth
.password
.clear(); // don't prefill
645 if ( !openPasswordDialog(connectionAuth
)) {
646 error(ERR_USER_CANCELED
,connectionHost
);
647 shutdownConnection();
652 connectionAuth
.password
+= ENDLINE
;
653 if (connectionAuth
.username
!= connectionUser
) {
655 dest
.setUser(connectionAuth
.username
);
656 dest
.setPass(connectionAuth
.password
);
658 if (isStat
) { // FIXME: just a workaround for konq deficiencies
659 redirectUser
= connectionAuth
.username
;
660 redirectPass
= connectionAuth
.password
;
663 commandCodes
.clear();
667 myDebug( << "sending pass" << endl
);
670 writeChild(connectionAuth
.password
.toLatin1(),connectionAuth
.password
.length());
678 } else if (buf
.endsWith('?')) {
679 int rc
= messageBox(QuestionYesNo
,thisFn
+buf
);
680 if (rc
== KMessageBox::Yes
) {
681 writeChild("yes\n",4);
683 writeChild("no\n",3);
690 myDebug( << "unmatched case in initial handling! should not happen!" << endl
);
693 if (buf
.endsWith("(y/n)")) {
694 int rc
= messageBox(QuestionYesNo
,thisFn
+buf
);
695 if (rc
== KMessageBox::Yes
) {
708 void fishProtocol::setHostInternal(const KUrl
& u
){
710 if(port
<= 0 ) // no port is -1 in QUrl, but in kde3 we used 0 and the kioslaves assume that.
712 setHost(u
.host(),port
,u
.user(),u
.pass());
716 sets connection information for subsequent commands
718 void fishProtocol::setHost(const QString
& host
, quint16 port
, const QString
& u
, const QString
& pass
){
721 local
= (host
== "localhost" && port
== 0);
722 if (user
.isEmpty()) user
= getenv("LOGNAME");
724 if (host
== connectionHost
&& port
== connectionPort
&& user
== connectionUser
)
726 myDebug( << "setHost " << u
<< "@" << host
<< endl
);
728 if (childPid
) shutdownConnection();
730 connectionHost
= host
;
731 connectionAuth
.url
.setHost(host
);
733 connectionUser
= user
;
734 connectionAuth
.username
= user
;
735 connectionAuth
.url
.setUser(user
);
737 connectionPort
= port
;
738 connectionPassword
= pass
;
743 Forced close of the connection
745 This function gets called from the application side of the universe,
746 it shouldn't send any response.
748 void fishProtocol::closeConnection(){
749 myDebug( << "closeConnection()" << endl
);
750 shutdownConnection(true);
754 Closes the connection
756 void fishProtocol::shutdownConnection(bool forced
){
759 childPid
->terminate();
761 int killStatus
= kill(childPid
,SIGTERM
); // We may not have permission...
762 if (killStatus
== 0) waitpid(childPid
, 0, 0);
766 ::close(childFd
); // ...in which case this should do the trick
772 infoMessage(i18n("Disconnected."));
780 commandCodes
.clear();
790 builds each FISH request and sets the error counter
792 bool fishProtocol::sendCommand(fish_command_type cmd
, ...) {
793 const fish_info
&info
= fishInfo
[cmd
];
794 myDebug( << "queuing: cmd="<< cmd
<< "['" << info
.command
<< "'](" << info
.params
<<"), alt=['" << info
.alt
<< "'], lines=" << info
.lines
<< endl
);
798 QString realCmd
= info
.command
;
799 QString realAlt
= info
.alt
;
800 static QRegExp
rx("[][\\\\\n $`#!()*?{}~&<>;'\"%^@|\t]");
801 for (int i
= 0; i
< info
.params
; i
++) {
802 QString
arg(va_arg(list
, const char *));
804 while ((pos
= rx
.indexIn(arg
,pos
+2)) >= 0) {
805 arg
.replace(pos
,0,QString("\\"));
807 //myDebug( << "arg " << i << ": " << arg << endl);
808 realCmd
.append(" ").append(arg
);
809 realAlt
.replace(QRegExp('%'+QString::number(i
+1)),arg
);
812 s
.append(realCmd
).append("\n ").append(realAlt
).append(" 2>&1;echo '### 000'\n");
813 if (realCmd
== "FISH")
815 commandList
.append(s
);
816 commandCodes
.append(cmd
);
821 checks response string for result code, converting 000 and 001 appropriately
823 int fishProtocol::handleResponse(const QString
&str
){
824 myDebug( << "handling: " << str
<< endl
);
825 if (str
.startsWith("### ")) {
827 int result
= str
.mid(4,3).toInt(&isOk
);
828 if (!isOk
) result
= 500;
829 if (result
== 0) result
= (errorCount
!= 0?500:200);
830 if (result
== 1) result
= (errorCount
!= 0?500:100);
831 myDebug( << "result: " << result
<< ", errorCount: " << errorCount
<< endl
);
839 int fishProtocol::makeTimeFromLs(const QString
&monthStr
, const QString
&dayStr
, const QString
&timeyearStr
)
841 QDateTime
dt(QDateTime::currentDateTime().toUTC());
842 int year
= dt
.date().year();
843 int month
= dt
.date().month();
844 int currentMonth
= month
;
845 int day
= dayStr
.toInt();
847 static const char * const monthNames
[12] = {
848 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
849 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
852 for (int i
=0; i
< 12; i
++) if (monthStr
.startsWith(monthNames
[i
])) {
857 int pos
= timeyearStr
.indexOf(':');
858 if (timeyearStr
.length() == 4 && pos
== -1) {
859 year
= timeyearStr
.toInt();
860 } else if (pos
== -1) {
863 if (month
> currentMonth
+ 1) year
--;
864 dt
.time().setHMS(timeyearStr
.left(pos
).toInt(),timeyearStr
.mid(pos
+1).toInt(),0);
866 dt
.date().setYMD(year
,month
,day
);
868 return dt
.toTime_t();
872 parses response from server and acts accordingly
874 void fishProtocol::manageConnection(const QString
&l
) {
876 int rc
= handleResponse(line
);
878 long pos
, pos2
, pos3
;
881 switch (fishCommand
) {
883 if (line
.startsWith("VER 0.0.3")) {
885 hasAppend
= line
.contains(" append ");
887 error(ERR_UNSUPPORTED_PROTOCOL
,line
);
888 shutdownConnection();
896 myDebug( << "listReason: " << static_cast<int>(listReason
) << endl
);
899 if (line
.length() > 0) {
900 switch (line
[0].cell()) {
912 long long val
= line
.toLongLong(&isOk
);
913 if (val
> 0 && isOk
) errorCount
--;
914 if ((fishCommand
== FISH_LIST
) && (listReason
== LIST
))
922 if (line
[1] == 'd') {
923 udsMime
= "inode/directory";
926 if (line
[1] == '-') {
928 } else if (line
[1] == 'l') {
930 } else if (line
[1] == 'c') {
932 } else if (line
[1] == 'b') {
934 } else if (line
[1] == 's') {
936 } else if (line
[1] == 'p') {
939 myDebug( << "unknown file type: " << line
[1].cell() << endl
);
944 //myDebug( << "file type: " << udsType << endl);
946 long long accessVal
= 0;
947 if (line
[2] == 'r') accessVal
|= S_IRUSR
;
948 if (line
[3] == 'w') accessVal
|= S_IWUSR
;
949 if (line
[4] == 'x' || line
[4] == 's') accessVal
|= S_IXUSR
;
950 if (line
[4] == 'S' || line
[4] == 's') accessVal
|= S_ISUID
;
951 if (line
[5] == 'r') accessVal
|= S_IRGRP
;
952 if (line
[6] == 'w') accessVal
|= S_IWGRP
;
953 if (line
[7] == 'x' || line
[7] == 's') accessVal
|= S_IXGRP
;
954 if (line
[7] == 'S' || line
[7] == 's') accessVal
|= S_ISGID
;
955 if (line
[8] == 'r') accessVal
|= S_IROTH
;
956 if (line
[9] == 'w') accessVal
|= S_IWOTH
;
957 if (line
[10] == 'x' || line
[10] == 't') accessVal
|= S_IXOTH
;
958 if (line
[10] == 'T' || line
[10] == 't') accessVal
|= S_ISVTX
;
959 udsEntry
.insert(KIO::UDSEntry::UDS_ACCESS
, accessVal
);
961 pos
= line
.indexOf('.',12);
966 udsEntry
.insert(KIO::UDSEntry::UDS_USER
, line
.mid(12,pos
-12));
967 udsEntry
.insert(KIO::UDSEntry::UDS_GROUP
, line
.mid(pos
+1));
972 pos
= line
.indexOf(' ');
973 pos2
= line
.indexOf(' ',pos
+1);
974 if (pos
< 0 || pos2
< 0) break;
976 udsEntry
.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME
,
977 makeTimeFromLs(line
.mid(1,pos
-1), line
.mid(pos
+1,pos2
-pos
), line
.mid(pos2
+1)));
981 pos
= line
.indexOf(' ');
982 pos2
= line
.indexOf(' ',pos
+1);
983 pos3
= line
.indexOf(' ',pos2
+1);
984 if (pos
< 0 || pos2
< 0 || pos3
< 0) break;
985 dt
.setDate(QDate(line
.mid(1,pos
-1).toInt(),line
.mid(pos
+1,pos2
-pos
-1).toInt(),line
.mid(pos2
+1,pos3
-pos2
-1).toInt()));
987 pos2
= line
.indexOf(' ',pos
+1);
988 pos3
= line
.indexOf(' ',pos2
+1);
989 if (pos
< 0 || pos2
< 0 || pos3
< 0) break;
990 dt
.setTime(QTime(line
.mid(pos
+1,pos2
-pos
-1).toInt(),line
.mid(pos2
+1,pos3
-pos2
-1).toInt(),line
.mid(pos3
+1).toInt()));
992 udsEntry
.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME
, dt
.toTime_t());
997 long long sizeVal
= line
.mid(1).toLongLong(&isOk
);
1000 udsEntry
.insert(KIO::UDSEntry::UDS_SIZE
, sizeVal
);
1009 pos
= line
.lastIndexOf('/');
1010 thisFn
= line
.mid(pos
< 0?1:pos
+1);
1011 if (fishCommand
== FISH_LIST
) {
1012 udsEntry
.insert(KIO::UDSEntry::UDS_NAME
, thisFn
);
1014 // By default, the mimetype comes from the extension
1015 // We'll use the file(1) result only as fallback [like the rest of KDE does]
1017 KUrl
kurl("fish://host/");
1018 kurl
.setFileName(thisFn
); // properly encode special chars
1019 KMimeType::Ptr mime
= KMimeType::findByUrl(kurl
, udsType
);
1020 if ( mime
->name() != KMimeType::defaultMimeType() )
1021 udsMime
= mime
->name();
1027 // This is getting ugly. file(1) makes some uneducated
1028 // guesses, so we must try to ignore them (#51274)
1029 if (udsMime
.isEmpty() && line
.right(8) != "/unknown" &&
1030 (thisFn
.indexOf('.') < 0 || (line
.left(8) != "Mtext/x-"
1031 && line
!= "Mtext/plain"))) {
1032 udsMime
= line
.mid(1);
1033 if ( udsMime
== "inode/directory" ) // a symlink to a dir is a dir
1040 udsEntry
.insert(KIO::UDSEntry::UDS_LINK_DEST
, line
.mid(1));
1041 if (!udsType
) udsType
= S_IFLNK
;
1046 if (!udsMime
.isNull())
1047 udsEntry
.insert(KIO::UDSEntry::UDS_MIME_TYPE
, udsMime
);
1050 udsEntry
.insert( KIO::UDSEntry::UDS_FILE_TYPE
, udsType
);
1053 if (fishCommand
== FISH_STAT
)
1054 udsStatEntry
= udsEntry
;
1055 else if (listReason
== LIST
) {
1056 listEntry(udsEntry
, false); //1
1057 } else if (listReason
== CHECK
) checkExist
= true; //0
1064 if (line
.length() == 0) {
1065 error(ERR_IS_DIRECTORY
,url
.prettyUrl());
1069 recvLen
= line
.toLongLong(&isOk
);
1071 error(ERR_COULD_NOT_READ
,url
.prettyUrl());
1072 shutdownConnection();
1079 } else if (rc
== 100) {
1080 switch (fishCommand
) {
1082 writeChild(fishCode
, fishCodeLen
);
1088 myDebug( << "reading " << recvLen
<< endl
);
1089 if (recvLen
== -1) {
1090 error(ERR_COULD_NOT_READ
,url
.prettyUrl());
1091 shutdownConnection();
1095 mimeTypeSent
= false;
1098 mimeType("application/x-zerosize");
1099 mimeTypeSent
= true;
1107 //myDebug( << "sending " << sendLen << endl);
1112 } else if (rc
/100 != 2) {
1113 switch (fishCommand
) {
1117 error(ERR_COULD_NOT_WRITE
,url
.prettyUrl());
1118 shutdownConnection();
1121 error(ERR_COULD_NOT_READ
,url
.prettyUrl());
1122 shutdownConnection();
1127 mimeType("inode/directory");
1128 mimeTypeSent
= true;
1134 error(ERR_COULD_NOT_READ
,url
.prettyUrl());
1135 shutdownConnection();
1140 error(ERR_SLAVE_DEFINED
,line
);
1141 shutdownConnection();
1145 error(ERR_CANNOT_ENTER_DIRECTORY
,url
.prettyUrl());
1148 myDebug( << "list error. reason: " << static_cast<int>(listReason
) << endl
);
1149 if (listReason
== LIST
) error(ERR_CANNOT_ENTER_DIRECTORY
,url
.prettyUrl());
1150 else if (listReason
== CHECK
) {
1156 error(ERR_DOES_NOT_EXIST
,url
.prettyUrl());
1157 udsStatEntry
.clear();
1160 error(ERR_CANNOT_CHMOD
,url
.prettyUrl());
1164 error(ERR_ACCESS_DENIED
,url
.prettyUrl());
1168 error(ERR_DIR_ALREADY_EXIST
,url
.prettyUrl());
1170 error(ERR_COULD_NOT_MKDIR
,url
.prettyUrl());
1173 error(ERR_COULD_NOT_RMDIR
,url
.prettyUrl());
1176 error(ERR_CANNOT_DELETE
,url
.prettyUrl());
1179 error(ERR_CANNOT_RENAME
,url
.prettyUrl());
1184 error(ERR_COULD_NOT_WRITE
,url
.prettyUrl());
1189 if (fishCommand
== FISH_STOR
) fishCommand
= (hasAppend
?FISH_APPEND
:FISH_WRITE
);
1190 if (fishCommand
== FISH_FISH
) {
1192 } else if (fishCommand
== FISH_LIST
) {
1193 if (listReason
== LIST
) {
1194 listEntry(UDSEntry(),true);
1195 } else if (listReason
== CHECK
) {
1196 if (!checkOverwrite
&& checkExist
)
1198 error(ERR_FILE_ALREADY_EXIST
,url
.prettyUrl());
1199 return; // Don't call finished!
1202 } else if (fishCommand
== FISH_STAT
) {
1203 udsStatEntry
.insert( KIO::UDSEntry::UDS_NAME
, url
.fileName() );
1204 statEntry(udsStatEntry
);
1205 } else if (fishCommand
== FISH_APPEND
) {
1207 if (readData(rawData
) > 0) sendCommand(FISH_APPEND
,E(QString::number(rawData
.size())),E(url
.path()));
1208 else if (!checkExist
&& putPerm
> -1) sendCommand(FISH_CHMOD
,E(QString::number(putPerm
,8)),E(url
.path()));
1209 sendLen
= rawData
.size();
1210 } else if (fishCommand
== FISH_WRITE
) {
1212 if (readData(rawData
) > 0) sendCommand(FISH_WRITE
,E(QString::number(putPos
)),E(QString::number(rawData
.size())),E(url
.path()));
1213 else if (!checkExist
&& putPerm
> -1) sendCommand(FISH_CHMOD
,E(QString::number(putPerm
,8)),E(url
.path()));
1214 putPos
+= rawData
.size();
1215 sendLen
= rawData
.size();
1216 } else if (fishCommand
== FISH_RETR
) {
1223 void fishProtocol::writeStdin(const QString
&line
)
1225 qlist
.append(line
.toLatin1());
1229 //myDebug( << "Writing: " << qlist.first().mid(0,qlist.first().indexOf('\n')) << endl);
1230 myDebug( << "Writing: " << qlist
.first() << endl
);
1231 myDebug( << "---------" << endl
);
1232 writeChild((const char *)qlist
.first(), qlist
.first().length());
1236 void fishProtocol::sent()
1239 myDebug( << "writing raw: " << rawData
.size() << "/" << rawWrite
<< endl
);
1240 writeChild(rawData
.data(),(rawWrite
> rawData
.size()?rawData
.size():rawWrite
));
1241 rawWrite
-= rawData
.size();
1244 if (readData(rawData
) <= 0) {
1245 shutdownConnection();
1249 } else if (rawWrite
== 0) {
1250 // workaround: some dd's insist in reading multiples of
1251 // 8 bytes, swallowing up to seven bytes. Sending
1252 // newlines is safe even when a sane dd is used
1253 writeChild("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",15);
1257 if (qlist
.count() > 0) qlist
.erase(qlist
.begin());
1258 if (qlist
.count() == 0) {
1261 //myDebug( << "Writing: " << qlist.first().mid(0,qlist.first().indexOf('\n')) << endl);
1262 myDebug( << "Writing: " << qlist
.first() << endl
);
1263 myDebug( << "---------" << endl
);
1264 writeChild((const char *)qlist
.first(),qlist
.first().length());
1268 int fishProtocol::received(const char *buffer
, KIO::fileoffset_t buflen
)
1272 if (buflen
<= 0) break;
1275 myDebug( << "processedSize " << dataRead
<< ", len " << buflen
<< "/" << rawRead
<< endl
);
1276 int dataSize
= (rawRead
> buflen
?buflen
:rawRead
);
1279 int mimeSize
= qMin(dataSize
, (int)(mimeBuffer
.size()-dataRead
));
1280 memcpy(mimeBuffer
.data()+dataRead
,buffer
,mimeSize
);
1281 dataRead
+= mimeSize
;
1282 rawRead
-= mimeSize
;
1285 if (rawRead
== 0) // End of data
1286 mimeBuffer
.resize(dataRead
);
1287 if (dataRead
< (int)mimeBuffer
.size())
1289 myDebug( << "wait for more" << endl
);
1292 sendmimeType(KMimeType::findByNameAndContent(url
.path(), mimeBuffer
)->name());
1293 mimeTypeSent
= true;
1294 if (fishCommand
!= FISH_READ
) {
1295 totalSize(dataRead
+ rawRead
);
1297 processedSize(dataRead
);
1299 mimeBuffer
.resize(1024);
1301 continue; // Process rest of buffer/buflen
1304 QByteArray
bdata(buffer
,dataSize
);
1307 dataRead
+= dataSize
;
1308 rawRead
-= dataSize
;
1309 processedSize(dataRead
);
1318 if (buflen
<= 0) break;
1322 while((pos
< buflen
) && (buffer
[pos
] != '\n'))
1327 QString s
= remoteEncoding()->decode(QByteArray(buffer
,pos
));
1332 manageConnection(s
);
1335 // Find next newline
1336 while((pos
< buflen
) && (buffer
[pos
] != '\n'))
1339 } while (childPid
&& buflen
&& (rawRead
> 0 || pos
< buflen
));
1343 void fishProtocol::get(const KUrl
& u
){
1344 myDebug( << "@@@@@@@@@ get " << u
<< endl
);
1348 if (!isLoggedIn
) return;
1350 if (!url
.hasPath()) {
1351 sendCommand(FISH_PWD
);
1354 sendCommand(FISH_RETR
,E(url
.path()));
1360 void fishProtocol::put(const KUrl
& u
, int permissions
, KIO::JobFlags flags
) {
1361 myDebug( << "@@@@@@@@@ put " << u
<< " " << permissions
<< " " << (flags
& KIO::Overwrite
) << " " /* << resume */ << endl
);
1366 if (!isLoggedIn
) return;
1368 if (!url
.hasPath()) {
1369 sendCommand(FISH_PWD
);
1371 putPerm
= permissions
;
1373 checkOverwrite
= flags
& KIO::Overwrite
;
1377 sendCommand(FISH_LIST
,E(url
.path()));
1378 sendCommand(FISH_STOR
,"0",E(url
.path()));
1380 const QString mtimeStr
= metaData( "modified" );
1381 if ( !mtimeStr
.isEmpty() ) {
1382 QDateTime dt
= QDateTime::fromString( mtimeStr
, Qt::ISODate
);
1383 // TODO set modification time on url.path() somehow
1384 // see FileProtocol::put if using utime() to do that.
1389 /** executes next command in sequence or calls finished() if all is done */
1390 void fishProtocol::finished() {
1391 if (commandList
.count() > 0) {
1392 fishCommand
= (fish_command_type
)commandCodes
.first();
1393 errorCount
= -fishInfo
[fishCommand
].lines
;
1397 udsStatEntry
.clear();
1398 writeStdin(commandList
.first());
1399 //if (fishCommand != FISH_APPEND && fishCommand != FISH_WRITE) infoMessage("Sending "+(commandList.first().mid(1,commandList.first().indexOf("\n")-1))+"...");
1400 commandList
.erase(commandList
.begin());
1401 commandCodes
.erase(commandCodes
.begin());
1403 myDebug( << "_______ emitting finished()" << endl
);
1404 SlaveBase::finished();
1408 /** aborts command sequence and calls error() */
1409 void fishProtocol::error(int type
, const QString
&detail
) {
1410 commandList
.clear();
1411 commandCodes
.clear();
1412 myDebug( << "ERROR: " << type
<< " - " << detail
<< endl
);
1413 SlaveBase::error(type
,detail
);
1416 /** executes a chain of commands */
1417 void fishProtocol::run() {
1430 FD_SET(childFd
,&rfds
);
1432 if (outBufPos
>= 0) FD_SET(childFd
,&wfds
);
1433 struct timeval timeout
;
1435 timeout
.tv_usec
= 1000;
1436 rc
= select(childFd
+1, &rfds
, &wfds
, NULL
, &timeout
);
1440 myDebug( << "select failed, rc: " << rc
<< ", error: " << strerror(errno
) << endl
);
1441 error(ERR_CONNECTION_BROKEN
,connectionHost
);
1442 shutdownConnection();
1445 if (FD_ISSET(childFd
,&wfds
) && outBufPos
>= 0) {
1447 if (outBufPos
>= 0) {
1451 debug
.setLatin1(outBuf
+outBufPos
,outBufLen
-outBufPos
);
1452 myDebug( << "now writing " << (outBufLen
-outBufPos
) << " " << debug
.left(40) << "..." << endl
);
1455 if (outBufLen
-outBufPos
> 0) rc
= ::write(childFd
,outBuf
+outBufPos
,outBufLen
-outBufPos
);
1457 if (outBufLen
-outBufPos
> 0) {
1458 rc
= childPid
->write(outBuf
);
1462 if (rc
>= 0) outBufPos
+= rc
;
1467 myDebug( << "write failed, rc: " << rc
<< ", error: " << strerror(errno
) << endl
);
1469 myDebug( << "write failed, rc: " << rc
);
1471 error(ERR_CONNECTION_BROKEN
,connectionHost
);
1472 shutdownConnection();
1475 if (outBufPos
>= outBufLen
) {
1482 else if (FD_ISSET(childFd
,&rfds
)) {
1483 rc
= ::read(childFd
,buf
+offset
,32768-offset
);
1485 else if (childPid
->waitForReadyRead(1000)) {
1486 rc
= childPid
->read(buf
+offset
,32768-offset
);
1488 //myDebug( << "read " << rc << " bytes" << endl);
1490 int noff
= received(buf
,rc
+offset
);
1491 if (noff
> 0) memmove(buf
,buf
+offset
+rc
-noff
,noff
);
1492 //myDebug( << "left " << noff << " bytes: " << QString::fromLatin1(buf,offset) << endl);
1498 myDebug( << "read failed, rc: " << rc
<< ", error: " << strerror(errno
) << endl
);
1500 myDebug( << "read failed, rc: " << rc
);
1502 error(ERR_CONNECTION_BROKEN
,connectionHost
);
1503 shutdownConnection();
1513 void fishProtocol::stat(const KUrl
& u
){
1514 myDebug( << "@@@@@@@@@ stat " << u
<< endl
);
1517 isStat
= true; // FIXME: just a workaround for konq deficiencies
1519 isStat
= false; // FIXME: just a workaround for konq deficiencies
1520 if (!isLoggedIn
) return;
1522 if (!url
.hasPath()) {
1523 sendCommand(FISH_PWD
);
1525 sendCommand(FISH_STAT
,E(url
.path(KUrl::RemoveTrailingSlash
)));
1529 /** find mimetype for a file */
1530 void fishProtocol::mimetype(const KUrl
& u
){
1531 myDebug( << "@@@@@@@@@ mimetype " << u
<< endl
);
1535 if (!isLoggedIn
) return;
1537 if (!url
.hasPath()) {
1538 sendCommand(FISH_PWD
);
1541 sendCommand(FISH_READ
,"0","1024",E(url
.path()));
1545 /** list a directory */
1546 void fishProtocol::listDir(const KUrl
& u
){
1547 myDebug( << "@@@@@@@@@ listDir " << u
<< endl
);
1551 if (!isLoggedIn
) return;
1553 if (!url
.hasPath()) {
1554 sendCommand(FISH_PWD
);
1557 sendCommand(FISH_LIST
,E(url
.path()));
1561 /** create a directory */
1562 void fishProtocol::mkdir(const KUrl
& u
, int permissions
) {
1563 myDebug( << "@@@@@@@@@ mkdir " << u
<< " " << permissions
<< endl
);
1567 if (!isLoggedIn
) return;
1569 if (!url
.hasPath()) {
1570 sendCommand(FISH_PWD
);
1572 sendCommand(FISH_MKD
,E(url
.path()));
1573 if (permissions
> -1) sendCommand(FISH_CHMOD
,E(QString::number(permissions
,8)),E(url
.path()));
1577 /** rename a file */
1578 void fishProtocol::rename(const KUrl
& s
, const KUrl
& d
, KIO::JobFlags flags
) {
1579 myDebug( << "@@@@@@@@@ rename " << s
<< " " << d
<< " " << (flags
& KIO::Overwrite
) << endl
);
1580 if (s
.host() != d
.host() || s
.port() != d
.port() || s
.user() != d
.user()) {
1581 error(ERR_UNSUPPORTED_ACTION
,s
.prettyUrl());
1587 if (!isLoggedIn
) return;
1591 if (!url
.hasPath()) {
1592 sendCommand(FISH_PWD
);
1594 if (!(flags
& KIO::Overwrite
)) {
1596 checkOverwrite
= false;
1597 sendCommand(FISH_LIST
,E(url
.path()));
1599 sendCommand(FISH_RENAME
,E(src
.path()),E(url
.path()));
1603 /** create a symlink */
1604 void fishProtocol::symlink(const QString
& target
, const KUrl
& u
, KIO::JobFlags flags
) {
1605 myDebug( << "@@@@@@@@@ symlink " << target
<< " " << u
<< " " << (flags
& KIO::Overwrite
) << endl
);
1609 if (!isLoggedIn
) return;
1611 if (!url
.hasPath()) {
1612 sendCommand(FISH_PWD
);
1614 if (!(flags
& KIO::Overwrite
)) {
1616 checkOverwrite
= false;
1617 sendCommand(FISH_LIST
,E(url
.path()));
1619 sendCommand(FISH_SYMLINK
,E(target
),E(url
.path()));
1623 /** change file permissions */
1624 void fishProtocol::chmod(const KUrl
& u
, int permissions
){
1625 myDebug( << "@@@@@@@@@ chmod " << u
<< " " << permissions
<< endl
);
1629 if (!isLoggedIn
) return;
1631 if (!url
.hasPath()) {
1632 sendCommand(FISH_PWD
);
1634 if (permissions
> -1) sendCommand(FISH_CHMOD
,E(QString::number(permissions
,8)),E(url
.path()));
1638 /** copies a file */
1639 void fishProtocol::copy(const KUrl
&s
, const KUrl
&d
, int permissions
, KIO::JobFlags flags
) {
1640 myDebug( << "@@@@@@@@@ copy " << s
<< " " << d
<< " " << permissions
<< " " << (flags
& KIO::Overwrite
) << endl
);
1641 if (s
.host() != d
.host() || s
.port() != d
.port() || s
.user() != d
.user()) {
1642 error(ERR_UNSUPPORTED_ACTION
,s
.prettyUrl());
1645 //myDebug( << s << endl << d << endl);
1649 if (!isLoggedIn
) return;
1653 if (!src
.hasPath()) {
1654 sendCommand(FISH_PWD
);
1656 if (!(flags
& KIO::Overwrite
)) {
1658 checkOverwrite
= false;
1659 sendCommand(FISH_LIST
,E(url
.path()));
1661 sendCommand(FISH_COPY
,E(src
.path()),E(url
.path()));
1662 if (permissions
> -1) sendCommand(FISH_CHMOD
,E(QString::number(permissions
,8)),E(url
.path()));
1666 /** removes a file or directory */
1667 void fishProtocol::del(const KUrl
&u
, bool isFile
){
1668 myDebug( << "@@@@@@@@@ del " << u
<< " " << isFile
<< endl
);
1672 if (!isLoggedIn
) return;
1674 if (!url
.hasPath()) {
1675 sendCommand(FISH_PWD
);
1677 sendCommand((isFile
?FISH_DELE
:FISH_RMD
),E(url
.path()));
1681 /** special like background execute */
1682 void fishProtocol::special( const QByteArray
&data
){
1685 QDataStream
stream(data
);
1689 case FISH_EXEC_CMD
: // SSH EXEC
1696 myDebug( << "@@@@@@@@@ exec " << u
<< " " << command
<< endl
);
1700 if (!isLoggedIn
) return;
1701 sendCommand(FISH_EXEC
,E(command
),E(url
.path()));
1706 // Some command we don't understand.
1707 error(ERR_UNSUPPORTED_ACTION
,QString().setNum(tmp
));
1711 /** report status */
1712 void fishProtocol::slave_status() {
1713 myDebug( << "@@@@@@@@@ slave_status" << endl
);
1715 slaveStatus(connectionHost
,isLoggedIn
);
1717 slaveStatus(QString(),false);