|
|
/***************************************************************************
|
|
|
fish.cpp - a FISH kioslave
|
|
|
-------------------
|
|
|
begin : Thu Oct 4 17:09:14 CEST 2001
|
|
|
copyright : (C) 2001-2003 by J<>rg Walter
|
|
|
email : jwalt-kde@garni.ch
|
|
|
***************************************************************************/
|
|
|
|
|
|
/***************************************************************************
|
|
|
* *
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
* the Free Software Foundation, version 2 of the License *
|
|
|
* *
|
|
|
***************************************************************************/
|
|
|
|
|
|
/*
|
|
|
This code contains fragments and ideas from the ftp kioslave
|
|
|
done by David Faure <faure@kde.org>.
|
|
|
|
|
|
Structure is a bit complicated, since I made the mistake to use
|
|
|
KProcess... now there is a lightweight homebrew async IO system
|
|
|
inside, but if signals/slots become available for ioslaves, switching
|
|
|
back to KProcess should be easy.
|
|
|
*/
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include <qcstring.h>
|
|
|
#include <qfile.h>
|
|
|
#include <qsocket.h>
|
|
|
#include <qdatetime.h>
|
|
|
#include <qbitarray.h>
|
|
|
#include <qregexp.h>
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
#ifdef HAVE_PTY_H
|
|
|
#include <pty.h>
|
|
|
#endif
|
|
|
#ifdef HAVE_TERMIOS_H
|
|
|
#include <termios.h>
|
|
|
#endif
|
|
|
#include <math.h>
|
|
|
#include <unistd.h>
|
|
|
#include <signal.h>
|
|
|
#include <sys/wait.h>
|
|
|
#include <sys/socket.h>
|
|
|
#include <netinet/in.h>
|
|
|
#include <netdb.h>
|
|
|
#include <sys/types.h>
|
|
|
#ifdef HAVE_STROPTS
|
|
|
#include <stropts.h>
|
|
|
#endif
|
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
|
#include <sys/ioctl.h>
|
|
|
#endif
|
|
|
#ifdef HAVE_LIBUTIL_H
|
|
|
#include <libutil.h>
|
|
|
#endif
|
|
|
#ifdef HAVE_UTIL_H
|
|
|
#include <util.h>
|
|
|
#endif
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
#include <kmessagebox.h>
|
|
|
#include <kinstance.h>
|
|
|
#include <kglobal.h>
|
|
|
#include <kstandarddirs.h>
|
|
|
#include <klocale.h>
|
|
|
#include <kremoteencoding.h>
|
|
|
#include <kurl.h>
|
|
|
#include <ksock.h>
|
|
|
#include <stdarg.h>
|
|
|
#include <time.h>
|
|
|
#include <sys/stat.h>
|
|
|
#include <kmimetype.h>
|
|
|
#include <kmimemagic.h>
|
|
|
#include <fcntl.h>
|
|
|
#include <sys/socket.h>
|
|
|
#include <errno.h>
|
|
|
#include <sys/resource.h>
|
|
|
|
|
|
#include "fish.h"
|
|
|
#include "fishcode.h"
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
#define myDebug(x) kdDebug(7127) << __LINE__ << ": " x
|
|
|
#define connected() do{myDebug( << "_______ emitting connected()" << endl); connected();}while(0)
|
|
|
#define dataReq() do{myDebug( << "_______ emitting dataReq()" << endl); dataReq();}while(0)
|
|
|
#define needSubURLData() do{myDebug( << "_______ emitting needSubURLData()" << endl); needSubURLData();}while(0)
|
|
|
#define slaveStatus(x,y) do{myDebug( << "_______ emitting slaveStatus(" << x << ", " << y << ")" << endl); slaveStatus(x,y);}while(0)
|
|
|
#define statEntry(x) do{myDebug( << "_______ emitting statEntry("<<x.size()<<")" << endl); statEntry(x);}while(0)
|
|
|
#define listEntries(x) do{myDebug( << "_______ emitting listEntries(...)" << endl); listEntries(x);}while(0)
|
|
|
#define canResume(x) do{myDebug( << "_______ emitting canResume("<<(int)x<<")" << endl); canResume(x);}while(0)
|
|
|
#define totalSize(x) do{myDebug( << "_______ emitting totalSize("<<(int)x<<")" << endl); totalSize(x);}while(0)
|
|
|
#define processedSize(x) do{myDebug( << "_______ emitting processedSize("<<x<<")" << endl); processedSize(x);}while(0)
|
|
|
#define speed(x) do{myDebug( << "_______ emitting speed("<<(int)x<<")" << endl); speed(x);}while(0)
|
|
|
#define redirection(x) do{myDebug( << "_______ emitting redirection("<<x<<")" << endl); redirection(x);}while(0)
|
|
|
#define errorPage() do{myDebug( << "_______ emitting errorPage()" << endl); errorPage();}while(0)
|
|
|
#define sendmimeType(x) do{myDebug( << "_______ emitting mimeType("<<x<<")" << endl); mimeType(x);}while(0)
|
|
|
#define warning(x) do{myDebug( << "_______ emitting warning("<<x<<")" << endl); warning(x);}while(0)
|
|
|
#define infoMessage(x) do{myDebug( << "_______ emitting infoMessage("<<x<<")" << endl); infoMessage(x);}while(0)
|
|
|
#else
|
|
|
#define myDebug(x)
|
|
|
#define sendmimeType(x) mimeType(x)
|
|
|
#endif
|
|
|
|
|
|
static char *sshPath = NULL;
|
|
|
static char *suPath = NULL;
|
|
|
// disabled: currently not needed. Didn't work reliably.
|
|
|
// static int isOpenSSH = 0;
|
|
|
|
|
|
static int isNXFish = 0;
|
|
|
|
|
|
#define E(x) ((const char*)remoteEncoding()->encode(x).data())
|
|
|
|
|
|
using namespace KIO;
|
|
|
extern "C" {
|
|
|
|
|
|
static void ripper(int)
|
|
|
{
|
|
|
while (waitpid(-1,0,WNOHANG) > 0) {
|
|
|
// do nothing, go on
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int KDE_EXPORT kdemain( int argc, char **argv )
|
|
|
{
|
|
|
KLocale::setMainCatalogue("kio_fish");
|
|
|
KInstance instance("fish");
|
|
|
|
|
|
myDebug( << "*** Starting fish " << endl);
|
|
|
if (argc != 4) {
|
|
|
myDebug( << "Usage: fish protocol domain-socket1 domain-socket2" << endl);
|
|
|
exit(-1);
|
|
|
}
|
|
|
|
|
|
setenv("TZ", "UTC", true);
|
|
|
|
|
|
struct sigaction act;
|
|
|
memset(&act,0,sizeof(act));
|
|
|
act.sa_handler = ripper;
|
|
|
act.sa_flags = 0
|
|
|
#ifdef SA_NOCLDSTOP
|
|
|
| SA_NOCLDSTOP
|
|
|
#endif
|
|
|
#ifdef SA_RESTART
|
|
|
| SA_RESTART
|
|
|
#endif
|
|
|
;
|
|
|
sigaction(SIGCHLD,&act,NULL);
|
|
|
|
|
|
if (qstrcmp(argv[1],"nxfish")==0) {
|
|
|
// Set NXFish - Mode
|
|
|
isNXFish=1;
|
|
|
}
|
|
|
|
|
|
fishProtocol slave(argv[2], argv[3]);
|
|
|
slave.dispatchLoop();
|
|
|
|
|
|
myDebug( << "*** fish Done" << endl);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const struct fishProtocol::fish_info fishProtocol::fishInfo[] = {
|
|
|
{ ("FISH"), 0,
|
|
|
("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;"),
|
|
|
1 },
|
|
|
{ ("VER 0.0.3 copy append lscount lslinks lsmime exec stat"), 0,
|
|
|
("echo 'VER 0.0.3 copy append lscount lslinks lsmime exec stat'"),
|
|
|
1 },
|
|
|
{ ("PWD"), 0,
|
|
|
("pwd"),
|
|
|
1 },
|
|
|
{ ("LIST"), 1,
|
|
|
("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; );"
|
|
|
"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; )"),
|
|
|
0 },
|
|
|
{ ("STAT"), 1,
|
|
|
("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; );"
|
|
|
"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; )"),
|
|
|
0 },
|
|
|
{ ("RETR"), 1,
|
|
|
("ls -l %1 2>&1 | ( read -r a b c d x e; echo $x ) 2>&1; echo '### 001'; cat %1"),
|
|
|
1 },
|
|
|
{ ("STOR"), 2,
|
|
|
("> %2; echo '### 001'; ( [ \"`expr %1 / 4096`\" -gt 0 ] && dd bs=4096 count=`expr %1 / 4096` 2>/dev/null;"
|
|
|
"[ \"`expr %1 % 4096`\" -gt 0 ] && dd bs=`expr %1 % 4096` count=1 2>/dev/null; ) | ( cat > %2 || echo Error $?; cat > /dev/null )"),
|
|
|
0 },
|
|
|
{ ("CWD"), 1,
|
|
|
("cd %1"),
|
|
|
0 },
|
|
|
{ ("CHMOD"), 2,
|
|
|
("chmod %1 %2"),
|
|
|
0 },
|
|
|
{ ("DELE"), 1,
|
|
|
("rm -f %1"),
|
|
|
0 },
|
|
|
{ ("MKD"), 1,
|
|
|
("mkdir %1"),
|
|
|
0 },
|
|
|
{ ("RMD"), 1,
|
|
|
("rmdir %1"),
|
|
|
0 },
|
|
|
{ ("RENAME"), 2,
|
|
|
("mv -f %1 %2"),
|
|
|
0 },
|
|
|
{ ("LINK"), 2,
|
|
|
("ln -f %1 %2"),
|
|
|
0 },
|
|
|
{ ("SYMLINK"), 2,
|
|
|
("ln -sf %1 %2"),
|
|
|
0 },
|
|
|
{ ("CHOWN"), 2,
|
|
|
("chown %1 %2"),
|
|
|
0 },
|
|
|
{ ("CHGRP"), 2,
|
|
|
("chgrp %1 %2"),
|
|
|
0 },
|
|
|
{ ("READ"), 3,
|
|
|
("echo '### 100';cat %3 /dev/zero | ( [ \"`expr %1 / 4096`\" -gt 0 ] && dd bs=4096 count=`expr %1 / 4096` >/dev/null;"
|
|
|
"[ \"`expr %1 % 4096`\" -gt 0 ] && dd bs=`expr %1 % 4096` count=1 >/dev/null;"
|
|
|
"dd bs=%2 count=1; ) 2>/dev/null;"),
|
|
|
0 },
|
|
|
// Yes, this is "ibs=1", since dd "count" is input blocks.
|
|
|
// On network connections, read() may not fill the buffer
|
|
|
// completely (no more data immediately available), but dd
|
|
|
// does ignore that fact by design. Sorry, writes are slow.
|
|
|
// OTOH, WRITE is not used by the current ioslave methods,
|
|
|
// we use APPEND.
|
|
|
{ ("WRITE"), 3,
|
|
|
(">> %3; echo '### 001'; ( [ %2 -gt 0 ] && dd ibs=1 obs=%2 count=%2 2>/dev/null ) | "
|
|
|
"( dd ibs=32768 obs=%1 seek=1 of=%3 2>/dev/null || echo Error $?; cat >/dev/null; )"),
|
|
|
0 },
|
|
|
{ ("COPY"), 2,
|
|
|
("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"),
|
|
|
0 },
|
|
|
{ ("APPEND"), 2,
|
|
|
(">> %2; echo '### 001'; ( [ %1 -gt 0 ] && dd ibs=1 obs=%1 count=%1 2> /dev/null; ) | ( cat >> %2 || echo Error $?; cat >/dev/null; )"),
|
|
|
0 },
|
|
|
{ ("EXEC"), 2,
|
|
|
("UMASK=`umask`; umask 077; touch %2; umask $UMASK; eval %1 < /dev/null > %2 2>&1; echo \"###RESULT: $?\" >> %2"),
|
|
|
0 }
|
|
|
};
|
|
|
|
|
|
fishProtocol::fishProtocol(const QCString &pool_socket, const QCString &app_socket)
|
|
|
: SlaveBase("fish", pool_socket, app_socket), mimeBuffer(1024),
|
|
|
mimeTypeSent(false)
|
|
|
{
|
|
|
myDebug( << "fishProtocol::fishProtocol()" << endl);
|
|
|
if (sshPath == NULL) {
|
|
|
// disabled: currently not needed. Didn't work reliably.
|
|
|
// isOpenSSH = !system("ssh -V 2>&1 | grep OpenSSH > /dev/null");
|
|
|
if (isNXFish)
|
|
|
sshPath = strdup(QFile::encodeName(KStandardDirs::findExe("nxfish")));
|
|
|
else
|
|
|
sshPath = strdup(QFile::encodeName(KStandardDirs::findExe("ssh")));
|
|
|
}
|
|
|
if (suPath == NULL) {
|
|
|
suPath = strdup(QFile::encodeName(KStandardDirs::findExe("su")));
|
|
|
}
|
|
|
childPid = 0;
|
|
|
connectionPort = 0;
|
|
|
isLoggedIn = false;
|
|
|
writeReady = true;
|
|
|
isRunning = false;
|
|
|
firstLogin = true;
|
|
|
errorCount = 0;
|
|
|
rawRead = 0;
|
|
|
rawWrite = -1;
|
|
|
recvLen = -1;
|
|
|
sendLen = -1;
|
|
|
setMultipleAuthCaching( true );
|
|
|
connectionAuth.keepPassword = true;
|
|
|
connectionAuth.url.setProtocol("fish");
|
|
|
outBufPos = -1;
|
|
|
outBuf = NULL;
|
|
|
outBufLen = 0;
|
|
|
typeAtom.m_uds = UDS_FILE_TYPE;
|
|
|
typeAtom.m_long = 0;
|
|
|
mimeAtom.m_uds = UDS_MIME_TYPE;
|
|
|
mimeAtom.m_long = 0;
|
|
|
mimeAtom.m_str = QString::null;
|
|
|
|
|
|
hasAppend = false;
|
|
|
|
|
|
isStat = false; // FIXME: just a workaround for konq deficiencies
|
|
|
redirectUser = ""; // FIXME: just a workaround for konq deficiencies
|
|
|
redirectPass = ""; // FIXME: just a workaround for konq deficiencies
|
|
|
fishCodeLen = strlen(fishCode);
|
|
|
}
|
|
|
/* ---------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
fishProtocol::~fishProtocol()
|
|
|
{
|
|
|
myDebug( << "fishProtocol::~fishProtocol()" << endl);
|
|
|
shutdownConnection(true);
|
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------------------------------- */
|
|
|
|
|
|
/**
|
|
|
Connects to a server and logs us in via SSH. Then starts FISH protocol.
|
|
|
*/
|
|
|
void fishProtocol::openConnection() {
|
|
|
if (childPid) return;
|
|
|
|
|
|
if (connectionHost.isEmpty() && !isNXFish)
|
|
|
{
|
|
|
error( KIO::ERR_UNKNOWN_HOST, QString::null );
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
infoMessage(i18n("Connecting..."));
|
|
|
|
|
|
myDebug( << "connecting to: " << connectionUser << "@" << connectionHost << ":" << connectionPort << endl);
|
|
|
sendCommand(FISH_FISH);
|
|
|
sendCommand(FISH_VER);
|
|
|
if (connectionStart()) {
|
|
|
error(ERR_COULD_NOT_CONNECT,connectionHost);
|
|
|
shutdownConnection();
|
|
|
return;
|
|
|
};
|
|
|
myDebug( << "subprocess is running" << endl);
|
|
|
}
|
|
|
|
|
|
static int open_pty_pair(int fd[2])
|
|
|
{
|
|
|
#if defined(HAVE_TERMIOS_H) && defined(HAVE_GRANTPT) && !defined(HAVE_OPENPTY)
|
|
|
/** with kind regards to The GNU C Library
|
|
|
Reference Manual for Version 2.2.x of the GNU C Library */
|
|
|
int master, slave;
|
|
|
char *name;
|
|
|
struct ::termios ti;
|
|
|
memset(&ti,0,sizeof(ti));
|
|
|
|
|
|
ti.c_cflag = CLOCAL|CREAD|CS8;
|
|
|
ti.c_cc[VMIN] = 1;
|
|
|
|
|
|
#ifdef HAVE_GETPT
|
|
|
master = getpt();
|
|
|
#else
|
|
|
master = open("/dev/ptmx", O_RDWR);
|
|
|
#endif
|
|
|
if (master < 0) return 0;
|
|
|
|
|
|
if (grantpt(master) < 0 || unlockpt(master) < 0) goto close_master;
|
|
|
|
|
|
name = ptsname(master);
|
|
|
if (name == NULL) goto close_master;
|
|
|
|
|
|
slave = open(name, O_RDWR);
|
|
|
if (slave == -1) goto close_master;
|
|
|
|
|
|
#if (defined(HAVE_ISASTREAM) || defined(isastream)) && defined(I_PUSH)
|
|
|
if (isastream(slave) &&
|
|
|
(ioctl(slave, I_PUSH, "ptem") < 0 ||
|
|
|
ioctl(slave, I_PUSH, "ldterm") < 0))
|
|
|
goto close_slave;
|
|
|
#endif
|
|
|
|
|
|
tcsetattr(slave, TCSANOW, &ti);
|
|
|
fd[0] = master;
|
|
|
fd[1] = slave;
|
|
|
return 0;
|
|
|
|
|
|
#if (defined(HAVE_ISASTREAM) || defined(isastream)) && defined(I_PUSH)
|
|
|
close_slave:
|
|
|
#endif
|
|
|
close(slave);
|
|
|
|
|
|
close_master:
|
|
|
close(master);
|
|
|
return -1;
|
|
|
#else
|
|
|
#ifdef HAVE_OPENPTY
|
|
|
struct ::termios ti;
|
|
|
memset(&ti,0,sizeof(ti));
|
|
|
|
|
|
ti.c_cflag = CLOCAL|CREAD|CS8;
|
|
|
ti.c_cc[VMIN] = 1;
|
|
|
|
|
|
return openpty(fd,fd+1,NULL,&ti,NULL);
|
|
|
#else
|
|
|
#ifdef __GNUC__
|
|
|
#warning "No tty support available. Password dialog won't work."
|
|
|
#endif
|
|
|
return socketpair(PF_UNIX,SOCK_STREAM,0,fd);
|
|
|
#endif
|
|
|
#endif
|
|
|
}
|
|
|
/**
|
|
|
creates the subprocess
|
|
|
*/
|
|
|
bool fishProtocol::connectionStart() {
|
|
|
int fd[2];
|
|
|
int rc, flags;
|
|
|
thisFn = QString::null;
|
|
|
|
|
|
rc = open_pty_pair(fd);
|
|
|
if (rc == -1) {
|
|
|
myDebug( << "socketpair failed, error: " << strerror(errno) << endl);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
if (!requestNetwork()) return true;
|
|
|
myDebug( << "Exec: " << (local ? suPath : sshPath) << " Port: " << connectionPort << " User: " << connectionUser << endl);
|
|
|
childPid = fork();
|
|
|
if (childPid == -1) {
|
|
|
myDebug( << "fork failed, error: " << strerror(errno) << endl);
|
|
|
close(fd[0]);
|
|
|
close(fd[1]);
|
|
|
childPid = 0;
|
|
|
dropNetwork();
|
|
|
return true;
|
|
|
}
|
|
|
if (childPid == 0) {
|
|
|
// taken from konsole, see TEPty.C for details
|
|
|
// note: if we're running on socket pairs,
|
|
|
// this will fail, but thats what we expect
|
|
|
|
|
|
for (int sig = 1; sig < NSIG; sig++) signal(sig,SIG_DFL);
|
|
|
|
|
|
struct rlimit rlp;
|
|
|
getrlimit(RLIMIT_NOFILE, &rlp);
|
|
|
for (int i = 0; i < (int)rlp.rlim_cur; i++)
|
|
|
if (i != fd[1]) close(i);
|
|
|
|
|
|
dup2(fd[1],0);
|
|
|
dup2(fd[1],1);
|
|
|
dup2(fd[1],2);
|
|
|
if (fd[1] > 2) close(fd[1]);
|
|
|
|
|
|
setsid();
|
|
|
|
|
|
#if defined(TIOCSCTTY)
|
|
|
ioctl(0, TIOCSCTTY, 0);
|
|
|
#endif
|
|
|
|
|
|
int pgrp = getpid();
|
|
|
#if defined( _AIX) || defined( __hpux)
|
|
|
tcsetpgrp(0, pgrp);
|
|
|
#else
|
|
|
ioctl(0, TIOCSPGRP, (char *)&pgrp);
|
|
|
#endif
|
|
|
|
|
|
const char *dev = ttyname(0);
|
|
|
setpgid(0,0);
|
|
|
if (dev) close(open(dev, O_WRONLY, 0));
|
|
|
setpgid(0,0);
|
|
|
|
|
|
if (local) {
|
|
|
execl(suPath, "su", "-", connectionUser.latin1(), "-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);
|
|
|
} else {
|
|
|
#define common_args "-l", connectionUser.latin1(), "-x", "-e", "none", \
|
|
|
"-q", connectionHost.latin1(), \
|
|
|
"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
|
|
|
// disabled: leave compression up to the client.
|
|
|
// (isOpenSSH?"-C":"+C"),
|
|
|
|
|
|
if (connectionPort)
|
|
|
execl(sshPath, "ssh", "-p", QString::number(connectionPort).latin1(), common_args);
|
|
|
else
|
|
|
execl(sshPath, "ssh", common_args);
|
|
|
#undef common_args
|
|
|
}
|
|
|
myDebug( << "could not exec! " << strerror(errno) << endl);
|
|
|
::exit(-1);
|
|
|
}
|
|
|
close(fd[1]);
|
|
|
rc = fcntl(fd[0],F_GETFL,&flags);
|
|
|
rc = fcntl(fd[0],F_SETFL,flags|O_NONBLOCK);
|
|
|
childFd = fd[0];
|
|
|
|
|
|
fd_set rfds, wfds;
|
|
|
FD_ZERO(&rfds);
|
|
|
FD_ZERO(&wfds);
|
|
|
char buf[32768];
|
|
|
int offset = 0;
|
|
|
while (!isLoggedIn) {
|
|
|
FD_SET(childFd,&rfds);
|
|
|
FD_ZERO(&wfds);
|
|
|
if (outBufPos >= 0) FD_SET(childFd,&wfds);
|
|
|
struct timeval timeout;
|
|
|
timeout.tv_sec = 0;
|
|
|
timeout.tv_usec = 1000;
|
|
|
rc = select(childFd+1, &rfds, &wfds, NULL, &timeout);
|
|
|
if (rc < 0) {
|
|
|
if (errno == EINTR)
|
|
|
continue;
|
|
|
myDebug( << "select failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
|
return true;
|
|
|
}
|
|
|
if (FD_ISSET(childFd,&wfds) && outBufPos >= 0) {
|
|
|
if (outBuf) rc = write(childFd,outBuf+outBufPos,outBufLen-outBufPos);
|
|
|
else rc = 0;
|
|
|
if (rc >= 0) outBufPos += rc;
|
|
|
else {
|
|
|
if (errno == EINTR)
|
|
|
continue;
|
|
|
myDebug( << "write failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
|
outBufPos = -1;
|
|
|
//return true;
|
|
|
}
|
|
|
if (outBufPos >= outBufLen) {
|
|
|
outBufPos = -1;
|
|
|
outBuf = NULL;
|
|
|
outBufLen = 0;
|
|
|
}
|
|
|
}
|
|
|
if (FD_ISSET(childFd,&rfds)) {
|
|
|
rc = read(childFd,buf+offset,32768-offset);
|
|
|
if (rc > 0) {
|
|
|
int noff = establishConnection(buf,rc+offset);
|
|
|
if (noff < 0) return false;
|
|
|
if (noff > 0) memmove(buf,buf+offset+rc-noff,noff);
|
|
|
offset = noff;
|
|
|
} else {
|
|
|
if (errno == EINTR)
|
|
|
continue;
|
|
|
myDebug( << "read failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
writes one chunk of data to stdin of child process
|
|
|
*/
|
|
|
void fishProtocol::writeChild(const char *buf, KIO::fileoffset_t len) {
|
|
|
if (outBufPos >= 0 && outBuf) {
|
|
|
#if 0
|
|
|
QString debug;
|
|
|
debug.setLatin1(outBuf,outBufLen);
|
|
|
if (len > 0) myDebug( << "write request while old one is pending, throwing away input (" << outBufLen << "," << outBufPos << "," << debug.left(10) << "...)" << endl);
|
|
|
#endif
|
|
|
return;
|
|
|
}
|
|
|
outBuf = buf;
|
|
|
outBufPos = 0;
|
|
|
outBufLen = len;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
manages initial communication setup including password queries
|
|
|
*/
|
|
|
int fishProtocol::establishConnection(char *buffer, KIO::fileoffset_t len) {
|
|
|
QString buf;
|
|
|
buf.setLatin1(buffer,len);
|
|
|
int pos;
|
|
|
// Strip trailing whitespace
|
|
|
while (buf.length() && (buf[buf.length()-1] == ' '))
|
|
|
buf.truncate(buf.length()-1);
|
|
|
|
|
|
myDebug( << "establishing: got " << buf << endl);
|
|
|
while (childPid && ((pos = buf.find('\n')) >= 0 ||
|
|
|
buf.endsWith(":") || buf.endsWith("?"))) {
|
|
|
pos++;
|
|
|
QString str = buf.left(pos);
|
|
|
buf = buf.mid(pos);
|
|
|
if (str == "\n")
|
|
|
continue;
|
|
|
if (str == "FISH:\n") {
|
|
|
thisFn = QString::null;
|
|
|
infoMessage(i18n("Initiating protocol..."));
|
|
|
if (!connectionAuth.password.isEmpty()) {
|
|
|
connectionAuth.password = connectionAuth.password.left(connectionAuth.password.length()-1);
|
|
|
cacheAuthentication(connectionAuth);
|
|
|
}
|
|
|
isLoggedIn = true;
|
|
|
return 0;
|
|
|
} else if (!str.isEmpty()) {
|
|
|
thisFn += str;
|
|
|
} else if (buf.endsWith(":")) {
|
|
|
if (!redirectUser.isEmpty() && connectionUser != redirectUser) {
|
|
|
KURL dest = url;
|
|
|
dest.setUser(redirectUser);
|
|
|
dest.setPass(redirectPass);
|
|
|
redirection(dest);
|
|
|
commandList.clear();
|
|
|
commandCodes.clear();
|
|
|
finished();
|
|
|
redirectUser = "";
|
|
|
redirectPass = "";
|
|
|
return -1;
|
|
|
} else if (!connectionPassword.isEmpty()) {
|
|
|
myDebug( << "sending cpass" << endl);
|
|
|
connectionAuth.password = connectionPassword+"\n";
|
|
|
connectionPassword = QString::null;
|
|
|
// su does not like receiving a password directly after sending
|
|
|
// the password prompt so we wait a while.
|
|
|
if (local)
|
|
|
sleep(1);
|
|
|
writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
|
|
|
} else {
|
|
|
myDebug( << "sending mpass" << endl);
|
|
|
connectionAuth.prompt = thisFn+buf;
|
|
|
if (local)
|
|
|
connectionAuth.caption = i18n("Local Login") + " - " + url.user() + "@" + url.host();
|
|
|
else
|
|
|
connectionAuth.caption = i18n("SSH Authorization") + " - " + url.user() + "@" + url.host();
|
|
|
if ((!firstLogin || !checkCachedAuthentication(connectionAuth))) {
|
|
|
connectionAuth.password = QString::null; // don't prefill
|
|
|
if ( !openPassDlg(connectionAuth)) {
|
|
|
error(ERR_USER_CANCELED,connectionHost);
|
|
|
shutdownConnection();
|
|
|
return -1;
|
|
|
}
|
|
|
}
|
|
|
firstLogin = false;
|
|
|
connectionAuth.password += "\n";
|
|
|
if (connectionAuth.username != connectionUser) {
|
|
|
KURL dest = url;
|
|
|
dest.setUser(connectionAuth.username);
|
|
|
dest.setPass(connectionAuth.password);
|
|
|
redirection(dest);
|
|
|
if (isStat) { // FIXME: just a workaround for konq deficiencies
|
|
|
redirectUser = connectionAuth.username;
|
|
|
redirectPass = connectionAuth.password;
|
|
|
}
|
|
|
commandList.clear();
|
|
|
commandCodes.clear();
|
|
|
finished();
|
|
|
return -1;
|
|
|
}
|
|
|
myDebug( << "sending pass" << endl);
|
|
|
if (local)
|
|
|
sleep(1);
|
|
|
writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
|
|
|
}
|
|
|
thisFn = QString::null;
|
|
|
return 0;
|
|
|
} else if (buf.endsWith("?")) {
|
|
|
int rc = messageBox(QuestionYesNo,thisFn+buf);
|
|
|
if (rc == KMessageBox::Yes) {
|
|
|
writeChild("yes\n",4);
|
|
|
} else {
|
|
|
writeChild("no\n",3);
|
|
|
}
|
|
|
thisFn = QString::null;
|
|
|
return 0;
|
|
|
} else {
|
|
|
myDebug( << "unmatched case in initial handling! shouldn't happen!" << endl);
|
|
|
}
|
|
|
}
|
|
|
return buf.length();
|
|
|
}
|
|
|
/**
|
|
|
sets connection information for subsequent commands
|
|
|
*/
|
|
|
void fishProtocol::setHost(const QString & host, int port, const QString & u, const QString & pass){
|
|
|
QString user(u);
|
|
|
|
|
|
if (isNXFish)
|
|
|
local = 0;
|
|
|
else
|
|
|
local = (host == "localhost" && port == 0);
|
|
|
|
|
|
if (port <= 0) port = 0;
|
|
|
if (user.isEmpty()) user = getenv("LOGNAME");
|
|
|
|
|
|
if (host == connectionHost && port == connectionPort && user == connectionUser)
|
|
|
return;
|
|
|
myDebug( << "setHost " << u << "@" << host << endl);
|
|
|
|
|
|
if (childPid) shutdownConnection();
|
|
|
|
|
|
connectionHost = host;
|
|
|
connectionAuth.url.setHost(host);
|
|
|
|
|
|
connectionUser = user;
|
|
|
connectionAuth.username = user;
|
|
|
connectionAuth.url.setUser(user);
|
|
|
|
|
|
connectionPort = port;
|
|
|
connectionPassword = pass;
|
|
|
firstLogin = true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
Forced close of the connection
|
|
|
|
|
|
This function gets called from the application side of the universe,
|
|
|
it shouldn't send any response.
|
|
|
*/
|
|
|
void fishProtocol::closeConnection(){
|
|
|
myDebug( << "closeConnection()" << endl);
|
|
|
shutdownConnection(true);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
Closes the connection
|
|
|
*/
|
|
|
void fishProtocol::shutdownConnection(bool forced){
|
|
|
if (childPid) {
|
|
|
kill(childPid,SIGTERM); // We may not have permission...
|
|
|
childPid = 0;
|
|
|
close(childFd); // ...in which case this should do the trick
|
|
|
childFd = -1;
|
|
|
if (!forced)
|
|
|
{
|
|
|
dropNetwork();
|
|
|
infoMessage(i18n("Disconnected."));
|
|
|
}
|
|
|
}
|
|
|
outBufPos = -1;
|
|
|
outBuf = NULL;
|
|
|
outBufLen = 0;
|
|
|
qlist.clear();
|
|
|
commandList.clear();
|
|
|
commandCodes.clear();
|
|
|
isLoggedIn = false;
|
|
|
writeReady = true;
|
|
|
isRunning = false;
|
|
|
rawRead = 0;
|
|
|
rawWrite = -1;
|
|
|
recvLen = -1;
|
|
|
sendLen = -1;
|
|
|
}
|
|
|
/**
|
|
|
builds each FISH request and sets the error counter
|
|
|
*/
|
|
|
bool fishProtocol::sendCommand(fish_command_type cmd, ...) {
|
|
|
const fish_info &info = fishInfo[cmd];
|
|
|
myDebug( << "queueing: cmd="<< cmd << "['" << info.command << "'](" << info.params <<"), alt=['" << info.alt << "'], lines=" << info.lines << endl);
|
|
|
|
|
|
va_list list;
|
|
|
va_start(list, cmd);
|
|
|
QString realCmd = info.command;
|
|
|
QString realAlt = info.alt;
|
|
|
static QRegExp rx("[][\\\\\n $`#!()*?{}~&<>;'\"%^@|\t]");
|
|
|
for (int i = 0; i < info.params; i++) {
|
|
|
QString arg(va_arg(list, const char *));
|
|
|
int pos = -2;
|
|
|
while ((pos = rx.search(arg,pos+2)) >= 0) {
|
|
|
arg.replace(pos,0,QString("\\"));
|
|
|
}
|
|
|
//myDebug( << "arg " << i << ": " << arg << endl);
|
|
|
realCmd.append(" ").append(arg);
|
|
|
realAlt.replace(QRegExp("%"+QString::number(i+1)),arg);
|
|
|
}
|
|
|
QString s("#");
|
|
|
s.append(realCmd).append("\n ").append(realAlt).append(" 2>&1;echo '### 000'\n");
|
|
|
if (realCmd == "FISH")
|
|
|
s.prepend(" ");
|
|
|
commandList.append(s);
|
|
|
commandCodes.append(cmd);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
checks response string for result code, converting 000 and 001 appropriately
|
|
|
*/
|
|
|
int fishProtocol::handleResponse(const QString &str){
|
|
|
myDebug( << "handling: " << str << endl);
|
|
|
if (str.startsWith("### ")) {
|
|
|
bool isOk = false;
|
|
|
int result = str.mid(4,3).toInt(&isOk);
|
|
|
if (!isOk) result = 500;
|
|
|
if (result == 0) result = (errorCount != 0?500:200);
|
|
|
if (result == 1) result = (errorCount != 0?500:100);
|
|
|
myDebug( << "result: " << result << ", errorCount: " << errorCount << endl);
|
|
|
return result;
|
|
|
} else {
|
|
|
errorCount++;
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int fishProtocol::makeTimeFromLs(const QString &monthStr, const QString &dayStr, const QString &timeyearStr)
|
|
|
{
|
|
|
QDateTime dt(QDate::currentDate(Qt::UTC));
|
|
|
int year = dt.date().year();
|
|
|
int month = dt.date().month();
|
|
|
int currentMonth = month;
|
|
|
int day = dayStr.toInt();
|
|
|
|
|
|
static const char * const monthNames[12] = {
|
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
|
|
};
|
|
|
|
|
|
for (int i=0; i < 12; i++) if (monthStr.startsWith(monthNames[i])) {
|
|
|
month = i+1;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
int pos = timeyearStr.find(':');
|
|
|
if (timeyearStr.length() == 4 && pos == -1) {
|
|
|
year = timeyearStr.toInt();
|
|
|
} else if (pos == -1) {
|
|
|
return 0;
|
|
|
} else {
|
|
|
if (month > currentMonth + 1) year--;
|
|
|
dt.time().setHMS(timeyearStr.left(pos).toInt(),timeyearStr.mid(pos+1).toInt(),0);
|
|
|
}
|
|
|
dt.date().setYMD(year,month,day);
|
|
|
|
|
|
return dt.toTime_t();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
parses response from server and acts accordingly
|
|
|
*/
|
|
|
void fishProtocol::manageConnection(const QString &l) {
|
|
|
QString line(l);
|
|
|
int rc = handleResponse(line);
|
|
|
UDSAtom atom;
|
|
|
QDateTime dt;
|
|
|
KIO::filesize_t fsize;
|
|
|
int pos, pos2, pos3;
|
|
|
bool isOk = false;
|
|
|
if (!rc) {
|
|
|
switch (fishCommand) {
|
|
|
case FISH_VER:
|
|
|
if (line.startsWith("VER 0.0.3")) {
|
|
|
line.append(" ");
|
|
|
hasAppend = line.contains(" append ");
|
|
|
} else {
|
|
|
error(ERR_UNSUPPORTED_PROTOCOL,line);
|
|
|
shutdownConnection();
|
|
|
}
|
|
|
break;
|
|
|
case FISH_PWD:
|
|
|
url.setPath(line);
|
|
|
redirection(url);
|
|
|
break;
|
|
|
case FISH_LIST:
|
|
|
myDebug( << "listReason: " << listReason << endl);
|
|
|
/* Fall through */
|
|
|
case FISH_STAT:
|
|
|
if (line.length() > 0) {
|
|
|
switch (line[0].cell()) {
|
|
|
case '0':
|
|
|
case '1':
|
|
|
case '2':
|
|
|
case '3':
|
|
|
case '4':
|
|
|
case '5':
|
|
|
case '6':
|
|
|
case '7':
|
|
|
case '8':
|
|
|
case '9':
|
|
|
fsize = line.toULongLong(&isOk);
|
|
|
if (fsize > 0 && isOk) errorCount--;
|
|
|
if ((fishCommand == FISH_LIST) && (listReason == LIST))
|
|
|
totalSize(fsize);
|
|
|
break;
|
|
|
|
|
|
case 'P':
|
|
|
errorCount--;
|
|
|
if (line[1] == 'd') {
|
|
|
mimeAtom.m_str = "inode/directory";
|
|
|
typeAtom.m_long = S_IFDIR;
|
|
|
} else {
|
|
|
if (line[1] == '-') {
|
|
|
typeAtom.m_long = S_IFREG;
|
|
|
} else if (line[1] == 'l') {
|
|
|
typeAtom.m_long = S_IFLNK;
|
|
|
} else if (line[1] == 'c') {
|
|
|
typeAtom.m_long = S_IFCHR;
|
|
|
} else if (line[1] == 'b') {
|
|
|
typeAtom.m_long = S_IFBLK;
|
|
|
} else if (line[1] == 's') {
|
|
|
typeAtom.m_long = S_IFSOCK;
|
|
|
} else if (line[1] == 'p') {
|
|
|
typeAtom.m_long = S_IFIFO;
|
|
|
} else {
|
|
|
myDebug( << "unknown file type: " << line[1].cell() << endl);
|
|
|
errorCount++;
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
//myDebug( << "file type: " << atom.m_long << endl);
|
|
|
//udsEntry.append(atom);
|
|
|
|
|
|
atom.m_uds = UDS_ACCESS;
|
|
|
atom.m_long = 0;
|
|
|
if (line[2] == 'r') atom.m_long |= S_IRUSR;
|
|
|
if (line[3] == 'w') atom.m_long |= S_IWUSR;
|
|
|
if (line[4] == 'x' || line[4] == 's') atom.m_long |= S_IXUSR;
|
|
|
if (line[4] == 'S' || line[4] == 's') atom.m_long |= S_ISUID;
|
|
|
if (line[5] == 'r') atom.m_long |= S_IRGRP;
|
|
|
if (line[6] == 'w') atom.m_long |= S_IWGRP;
|
|
|
if (line[7] == 'x' || line[7] == 's') atom.m_long |= S_IXGRP;
|
|
|
if (line[7] == 'S' || line[7] == 's') atom.m_long |= S_ISGID;
|
|
|
if (line[8] == 'r') atom.m_long |= S_IROTH;
|
|
|
if (line[9] == 'w') atom.m_long |= S_IWOTH;
|
|
|
if (line[10] == 'x' || line[10] == 't') atom.m_long |= S_IXOTH;
|
|
|
if (line[10] == 'T' || line[10] == 't') atom.m_long |= S_ISVTX;
|
|
|
udsEntry.append(atom);
|
|
|
|
|
|
atom.m_uds = UDS_USER;
|
|
|
atom.m_long = 0;
|
|
|
pos = line.find('.',12);
|
|
|
if (pos < 0) {
|
|
|
errorCount++;
|
|
|
break;
|
|
|
}
|
|
|
atom.m_str = line.mid(12,pos-12);
|
|
|
udsEntry.append(atom);
|
|
|
|
|
|
atom.m_uds = UDS_GROUP;
|
|
|
atom.m_long = 0;
|
|
|
atom.m_str = line.mid(pos+1);
|
|
|
udsEntry.append(atom);
|
|
|
break;
|
|
|
|
|
|
case 'd':
|
|
|
atom.m_uds = UDS_MODIFICATION_TIME;
|
|
|
pos = line.find(' ');
|
|
|
pos2 = line.find(' ',pos+1);
|
|
|
if (pos < 0 || pos2 < 0) break;
|
|
|
errorCount--;
|
|
|
atom.m_long = makeTimeFromLs(line.mid(1,pos-1), line.mid(pos+1,pos2-pos), line.mid(pos2+1));
|
|
|
udsEntry.append(atom);
|
|
|
break;
|
|
|
|
|
|
case 'D':
|
|
|
atom.m_uds = UDS_MODIFICATION_TIME;
|
|
|
pos = line.find(' ');
|
|
|
pos2 = line.find(' ',pos+1);
|
|
|
pos3 = line.find(' ',pos2+1);
|
|
|
if (pos < 0 || pos2 < 0 || pos3 < 0) break;
|
|
|
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()));
|
|
|
pos = pos3;
|
|
|
pos2 = line.find(' ',pos+1);
|
|
|
pos3 = line.find(' ',pos2+1);
|
|
|
if (pos < 0 || pos2 < 0 || pos3 < 0) break;
|
|
|
dt.setTime(QTime(line.mid(pos+1,pos2-pos-1).toInt(),line.mid(pos2+1,pos3-pos2-1).toInt(),line.mid(pos3+1).toInt()));
|
|
|
errorCount--;
|
|
|
atom.m_long = dt.toTime_t();
|
|
|
udsEntry.append(atom);
|
|
|
break;
|
|
|
|
|
|
case 'S':
|
|
|
atom.m_uds = UDS_SIZE;
|
|
|
atom.m_long = line.mid(1).toULongLong(&isOk);
|
|
|
if (!isOk) break;
|
|
|
errorCount--;
|
|
|
udsEntry.append(atom);
|
|
|
break;
|
|
|
|
|
|
case 'E':
|
|
|
errorCount--;
|
|
|
break;
|
|
|
|
|
|
case ':':
|
|
|
atom.m_uds = UDS_NAME;
|
|
|
atom.m_long = 0;
|
|
|
pos = line.findRev('/');
|
|
|
atom.m_str = thisFn = line.mid(pos < 0?1:pos+1);
|
|
|
if (fishCommand == FISH_LIST)
|
|
|
udsEntry.append(atom);
|
|
|
// By default, the mimetype comes from the extension
|
|
|
// We'll use the file(1) result only as fallback [like the rest of KDE does]
|
|
|
{
|
|
|
KURL kurl("fish://host/");
|
|
|
kurl.setFileName(thisFn); // properly encode special chars
|
|
|
KMimeType::Ptr mime = KMimeType::findByURL(kurl);
|
|
|
if ( mime->name() != KMimeType::defaultMimeType() )
|
|
|
mimeAtom.m_str = mime->name();
|
|
|
}
|
|
|
errorCount--;
|
|
|
break;
|
|
|
|
|
|
case 'M': {
|
|
|
QString type = line.mid(1);
|
|
|
|
|
|
// First thing's first. If remote says this is a directory, throw out any
|
|
|
// name-based file type guesses.
|
|
|
if (type == "inode/directory" && mimeAtom.m_str != type) {
|
|
|
mimeAtom.m_str = type;
|
|
|
typeAtom.m_long = S_IFDIR;
|
|
|
}
|
|
|
// This is getting ugly. file(1) makes some uneducated
|
|
|
// guesses, so we must try to ignore them (#51274)
|
|
|
else if (mimeAtom.m_str.isEmpty() && line.right(8) != "/unknown" &&
|
|
|
(thisFn.find('.') < 0 || (line.left(8) != "Mtext/x-"
|
|
|
&& line != "Mtext/plain"))) {
|
|
|
mimeAtom.m_str = type;
|
|
|
}
|
|
|
errorCount--;
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
case 'L':
|
|
|
atom.m_uds = UDS_LINK_DEST;
|
|
|
atom.m_long = 0;
|
|
|
atom.m_str = line.mid(1);
|
|
|
udsEntry.append(atom);
|
|
|
if (!typeAtom.m_long) typeAtom.m_long = S_IFLNK;
|
|
|
errorCount--;
|
|
|
break;
|
|
|
}
|
|
|
} else {
|
|
|
if (!mimeAtom.m_str.isNull())
|
|
|
udsEntry.append(mimeAtom);
|
|
|
mimeAtom.m_str = QString::null;
|
|
|
|
|
|
udsEntry.append(typeAtom);
|
|
|
typeAtom.m_long = 0;
|
|
|
|
|
|
if (fishCommand == FISH_STAT)
|
|
|
udsStatEntry = udsEntry;
|
|
|
else if (listReason == LIST) {
|
|
|
listEntry(udsEntry, false); //1
|
|
|
} else if (listReason == CHECK) checkExist = true; //0
|
|
|
errorCount--;
|
|
|
udsEntry.clear();
|
|
|
}
|
|
|
break;
|
|
|
|
|
|
case FISH_RETR:
|
|
|
if (line.length() == 0) {
|
|
|
error(ERR_IS_DIRECTORY,url.prettyURL());
|
|
|
recvLen = 0;
|
|
|
break;
|
|
|
}
|
|
|
recvLen = line.toLongLong(&isOk);
|
|
|
if (!isOk) {
|
|
|
error(ERR_COULD_NOT_READ,url.prettyURL());
|
|
|
shutdownConnection();
|
|
|
break;
|
|
|
}
|
|
|
break;
|
|
|
default : break;
|
|
|
}
|
|
|
|
|
|
} else if (rc == 100) {
|
|
|
switch (fishCommand) {
|
|
|
case FISH_FISH:
|
|
|
writeChild(fishCode, fishCodeLen);
|
|
|
break;
|
|
|
case FISH_READ:
|
|
|
recvLen = 1024;
|
|
|
/* fall through */
|
|
|
case FISH_RETR:
|
|
|
myDebug( << "reading " << recvLen << endl);
|
|
|
if (recvLen == -1) {
|
|
|
error(ERR_COULD_NOT_READ,url.prettyURL());
|
|
|
shutdownConnection();
|
|
|
} else {
|
|
|
rawRead = recvLen;
|
|
|
dataRead = 0;
|
|
|
mimeTypeSent = false;
|
|
|
if (recvLen == 0)
|
|
|
{
|
|
|
mimeType("application/x-zerosize");
|
|
|
mimeTypeSent = true;
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
case FISH_STOR:
|
|
|
case FISH_WRITE:
|
|
|
case FISH_APPEND:
|
|
|
rawWrite = sendLen;
|
|
|
//myDebug( << "sending " << sendLen << endl);
|
|
|
writeChild(NULL,0);
|
|
|
break;
|
|
|
default : break;
|
|
|
}
|
|
|
} else if (rc/100 != 2) {
|
|
|
switch (fishCommand) {
|
|
|
case FISH_STOR:
|
|
|
case FISH_WRITE:
|
|
|
case FISH_APPEND:
|
|
|
error(ERR_COULD_NOT_WRITE,url.prettyURL());
|
|
|
shutdownConnection();
|
|
|
break;
|
|
|
case FISH_RETR:
|
|
|
error(ERR_COULD_NOT_READ,url.prettyURL());
|
|
|
shutdownConnection();
|
|
|
break;
|
|
|
case FISH_READ:
|
|
|
if ( rc == 501 )
|
|
|
{
|
|
|
mimeType("inode/directory");
|
|
|
mimeTypeSent = true;
|
|
|
recvLen = 0;
|
|
|
finished();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
error(ERR_COULD_NOT_READ,url.prettyURL());
|
|
|
shutdownConnection();
|
|
|
}
|
|
|
break;
|
|
|
case FISH_FISH:
|
|
|
case FISH_VER:
|
|
|
error(ERR_SLAVE_DEFINED,line);
|
|
|
shutdownConnection();
|
|
|
break;
|
|
|
case FISH_PWD:
|
|
|
case FISH_CWD:
|
|
|
error(ERR_CANNOT_ENTER_DIRECTORY,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_LIST:
|
|
|
myDebug( << "list error. reason: " << listReason << endl);
|
|
|
if (listReason == LIST) error(ERR_CANNOT_ENTER_DIRECTORY,url.prettyURL());
|
|
|
else if (listReason == CHECK) {
|
|
|
checkExist = false;
|
|
|
finished();
|
|
|
}
|
|
|
break;
|
|
|
case FISH_STAT:
|
|
|
error(ERR_DOES_NOT_EXIST,url.prettyURL());
|
|
|
udsStatEntry.clear();
|
|
|
break;
|
|
|
case FISH_CHMOD:
|
|
|
error(ERR_CANNOT_CHMOD,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_CHOWN:
|
|
|
case FISH_CHGRP:
|
|
|
error(ERR_ACCESS_DENIED,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_MKD:
|
|
|
if ( rc == 501 )
|
|
|
error(ERR_DIR_ALREADY_EXIST,url.prettyURL());
|
|
|
else
|
|
|
error(ERR_COULD_NOT_MKDIR,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_RMD:
|
|
|
error(ERR_COULD_NOT_RMDIR,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_DELE:
|
|
|
error(ERR_CANNOT_DELETE,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_RENAME:
|
|
|
error(ERR_CANNOT_RENAME,url.prettyURL());
|
|
|
break;
|
|
|
case FISH_COPY:
|
|
|
case FISH_LINK:
|
|
|
case FISH_SYMLINK:
|
|
|
error(ERR_COULD_NOT_WRITE,url.prettyURL());
|
|
|
break;
|
|
|
default : break;
|
|
|
}
|
|
|
} else {
|
|
|
if (fishCommand == FISH_STOR) fishCommand = (hasAppend?FISH_APPEND:FISH_WRITE);
|
|
|
if (fishCommand == FISH_FISH) {
|
|
|
connected();
|
|
|
} else if (fishCommand == FISH_LIST) {
|
|
|
if (listReason == LIST) {
|
|
|
listEntry(UDSEntry(),true);
|
|
|
} else if (listReason == CHECK) {
|
|
|
if (!checkOverwrite && checkExist)
|
|
|
{
|
|
|
error(ERR_FILE_ALREADY_EXIST,url.prettyURL());
|
|
|
return; // Don't call finished!
|
|
|
}
|
|
|
}
|
|
|
} else if (fishCommand == FISH_STAT) {
|
|
|
UDSAtom atom;
|
|
|
|
|
|
atom.m_uds = KIO::UDS_NAME;
|
|
|
atom.m_str = url.fileName();
|
|
|
udsStatEntry.append( atom );
|
|
|
statEntry(udsStatEntry);
|
|
|
} else if (fishCommand == FISH_APPEND) {
|
|
|
dataReq();
|
|
|
if (readData(rawData) > 0) sendCommand(FISH_APPEND,E(QString::number(rawData.size())),E(url.path()));
|
|
|
else if (!checkExist && putPerm > -1) sendCommand(FISH_CHMOD,E(QString::number(putPerm,8)),E(url.path()));
|
|
|
sendLen = rawData.size();
|
|
|
} else if (fishCommand == FISH_WRITE) {
|
|
|
dataReq();
|
|
|
if (readData(rawData) > 0) sendCommand(FISH_WRITE,E(QString::number(putPos)),E(QString::number(rawData.size())),E(url.path()));
|
|
|
else if (!checkExist && putPerm > -1) sendCommand(FISH_CHMOD,E(QString::number(putPerm,8)),E(url.path()));
|
|
|
putPos += rawData.size();
|
|
|
sendLen = rawData.size();
|
|
|
} else if (fishCommand == FISH_RETR) {
|
|
|
data(QByteArray());
|
|
|
}
|
|
|
finished();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void fishProtocol::writeStdin(const QString &line)
|
|
|
{
|
|
|
qlist.append(line);
|
|
|
|
|
|
if (writeReady) {
|
|
|
writeReady = false;
|
|
|
//myDebug( << "Writing: " << qlist.first().mid(0,qlist.first().find('\n')) << endl);
|
|
|
myDebug( << "Writing: " << qlist.first() << endl);
|
|
|
myDebug( << "---------" << endl);
|
|
|
writeChild((const char *)qlist.first().latin1(), qlist.first().length());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void fishProtocol::sent()
|
|
|
{
|
|
|
if (rawWrite > 0) {
|
|
|
myDebug( << "writing raw: " << rawData.size() << "/" << rawWrite << endl);
|
|
|
writeChild(rawData.data(),(rawWrite > rawData.size()?rawData.size():rawWrite));
|
|
|
rawWrite -= rawData.size();
|
|
|
if (rawWrite > 0) {
|
|
|
dataReq();
|
|
|
if (readData(rawData) <= 0) {
|
|
|
shutdownConnection();
|
|
|
}
|
|
|
}
|
|
|
return;
|
|
|
} else if (rawWrite == 0) {
|
|
|
// workaround: some dd's insist in reading multiples of
|
|
|
// 8 bytes, swallowing up to seven bytes. Sending
|
|
|
// newlines is safe even when a sane dd is used
|
|
|
writeChild("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",15);
|
|
|
rawWrite = -1;
|
|
|
return;
|
|
|
}
|
|
|
if (qlist.count() > 0) qlist.remove(qlist.begin());
|
|
|
if (qlist.count() == 0) {
|
|
|
writeReady = true;
|
|
|
} else {
|
|
|
//myDebug( << "Writing: " << qlist.first().mid(0,qlist.first().find('\n')) << endl);
|
|
|
myDebug( << "Writing: " << qlist.first() << endl);
|
|
|
myDebug( << "---------" << endl);
|
|
|
writeChild((const char *)qlist.first().latin1(),qlist.first().length());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
int fishProtocol::received(const char *buffer, KIO::fileoffset_t buflen)
|
|
|
{
|
|
|
int pos = 0;
|
|
|
do {
|
|
|
if (buflen <= 0) break;
|
|
|
|
|
|
if (rawRead > 0) {
|
|
|
//myDebug( << "processedSize " << dataRead << ", len " << buflen << "/" << rawRead << endl);
|
|
|
int dataSize = (rawRead > buflen?buflen:rawRead);
|
|
|
if (!mimeTypeSent)
|
|
|
{
|
|
|
int mimeSize = QMIN(dataSize, (int)mimeBuffer.size()-dataRead);
|
|
|
memcpy(mimeBuffer.data()+dataRead,buffer,mimeSize);
|
|
|
dataRead += mimeSize;
|
|
|
rawRead -= mimeSize;
|
|
|
buffer += mimeSize;
|
|
|
buflen -= mimeSize;
|
|
|
if (rawRead == 0) // End of data
|
|
|
mimeBuffer.resize(dataRead);
|
|
|
if (dataRead < (int)mimeBuffer.size())
|
|
|
{
|
|
|
myDebug( << "wait for more" << endl);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// We need a KMimeType::findByNameAndContent(filename,data)
|
|
|
// For now we do: find by extension, and if not found (or extension not reliable)
|
|
|
// then find by content.
|
|
|
bool accurate = false;
|
|
|
KMimeType::Ptr mime = KMimeType::findByURL( url, 0, false, true, &accurate );
|
|
|
if ( !mime || mime->name() == KMimeType::defaultMimeType()
|
|
|
|| !accurate )
|
|
|
{
|
|
|
KMimeType::Ptr p_mimeType = KMimeType::findByContent(mimeBuffer);
|
|
|
if ( p_mimeType && p_mimeType->name() != KMimeType::defaultMimeType() )
|
|
|
mime = p_mimeType;
|
|
|
}
|
|
|
|
|
|
sendmimeType(mime->name());
|
|
|
|
|
|
|
|
|
mimeTypeSent = true;
|
|
|
if (fishCommand != FISH_READ) {
|
|
|
totalSize(dataRead + rawRead);
|
|
|
data(mimeBuffer);
|
|
|
processedSize(dataRead);
|
|
|
}
|
|
|
mimeBuffer.resize(1024);
|
|
|
pos = 0;
|
|
|
continue; // Process rest of buffer/buflen
|
|
|
}
|
|
|
|
|
|
QByteArray bdata;
|
|
|
bdata.duplicate(buffer,dataSize);
|
|
|
data(bdata);
|
|
|
|
|
|
dataRead += dataSize;
|
|
|
rawRead -= dataSize;
|
|
|
processedSize(dataRead);
|
|
|
if (rawRead <= 0) {
|
|
|
buffer += dataSize;
|
|
|
buflen -= dataSize;
|
|
|
} else {
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (buflen <= 0) break;
|
|
|
|
|
|
pos = 0;
|
|
|
// Find newline
|
|
|
while((pos < buflen) && (buffer[pos] != '\n'))
|
|
|
++pos;
|
|
|
|
|
|
if (pos < buflen)
|
|
|
{
|
|
|
QString s = remoteEncoding()->decode(QCString(buffer,pos+1));
|
|
|
|
|
|
buffer += pos+1;
|
|
|
buflen -= pos+1;
|
|
|
|
|
|
manageConnection(s);
|
|
|
|
|
|
pos = 0;
|
|
|
// Find next newline
|
|
|
while((pos < buflen) && (buffer[pos] != '\n'))
|
|
|
++pos;
|
|
|
}
|
|
|
} while (childPid && buflen && (rawRead > 0 || pos < buflen));
|
|
|
return buflen;
|
|
|
}
|
|
|
/** get a file */
|
|
|
void fishProtocol::get(const KURL& u){
|
|
|
myDebug( << "@@@@@@@@@ get " << u << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
recvLen = -1;
|
|
|
sendCommand(FISH_RETR,E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
|
|
|
/** put a file */
|
|
|
void fishProtocol::put(const KURL& u, int permissions, bool overwrite, bool /*resume*/){
|
|
|
myDebug( << "@@@@@@@@@ put " << u << " " << permissions << " " << overwrite << " " /* << resume */ << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
putPerm = permissions;
|
|
|
checkOverwrite = overwrite;
|
|
|
checkExist = false;
|
|
|
putPos = 0;
|
|
|
listReason = CHECK;
|
|
|
sendCommand(FISH_LIST,E(url.path()));
|
|
|
sendCommand(FISH_STOR,"0",E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** executes next command in sequence or calls finished() if all is done */
|
|
|
void fishProtocol::finished() {
|
|
|
if (commandList.count() > 0) {
|
|
|
fishCommand = (fish_command_type)commandCodes.first();
|
|
|
errorCount = -fishInfo[fishCommand].lines;
|
|
|
rawRead = 0;
|
|
|
rawWrite = -1;
|
|
|
udsEntry.clear();
|
|
|
udsStatEntry.clear();
|
|
|
writeStdin(commandList.first());
|
|
|
//if (fishCommand != FISH_APPEND && fishCommand != FISH_WRITE) infoMessage("Sending "+(commandList.first().mid(1,commandList.first().find("\n")-1))+"...");
|
|
|
commandList.remove(commandList.begin());
|
|
|
commandCodes.remove(commandCodes.begin());
|
|
|
} else {
|
|
|
myDebug( << "_______ emitting finished()" << endl);
|
|
|
SlaveBase::finished();
|
|
|
isRunning = false;
|
|
|
}
|
|
|
}
|
|
|
/** aborts command sequence and calls error() */
|
|
|
void fishProtocol::error(int type, const QString &detail) {
|
|
|
commandList.clear();
|
|
|
commandCodes.clear();
|
|
|
myDebug( << "ERROR: " << type << " - " << detail << endl);
|
|
|
SlaveBase::error(type,detail);
|
|
|
isRunning = false;
|
|
|
}
|
|
|
/** executes a chain of commands */
|
|
|
void fishProtocol::run() {
|
|
|
if (!isRunning) {
|
|
|
int rc;
|
|
|
isRunning = true;
|
|
|
finished();
|
|
|
fd_set rfds, wfds;
|
|
|
FD_ZERO(&rfds);
|
|
|
char buf[32768];
|
|
|
int offset = 0;
|
|
|
while (isRunning) {
|
|
|
FD_SET(childFd,&rfds);
|
|
|
FD_ZERO(&wfds);
|
|
|
if (outBufPos >= 0) FD_SET(childFd,&wfds);
|
|
|
struct timeval timeout;
|
|
|
timeout.tv_sec = 0;
|
|
|
timeout.tv_usec = 1000;
|
|
|
rc = select(childFd+1, &rfds, &wfds, NULL, &timeout);
|
|
|
if (rc < 0) {
|
|
|
if (errno == EINTR)
|
|
|
continue;
|
|
|
myDebug( << "select failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
|
error(ERR_CONNECTION_BROKEN,connectionHost);
|
|
|
shutdownConnection();
|
|
|
return;
|
|
|
}
|
|
|
if (FD_ISSET(childFd,&wfds) && outBufPos >= 0) {
|
|
|
#if 0
|
|
|
QString debug;
|
|
|
debug.setLatin1(outBuf+outBufPos,outBufLen-outBufPos);
|
|
|
myDebug( << "now writing " << (outBufLen-outBufPos) << " " << debug.left(40) << "..." << endl);
|
|
|
#endif
|
|
|
if (outBufLen-outBufPos > 0) rc = write(childFd,outBuf+outBufPos,outBufLen-outBufPos);
|
|
|
else rc = 0;
|
|
|
if (rc >= 0) outBufPos += rc;
|
|
|
else {
|
|
|
if (errno == EINTR)
|
|
|
continue;
|
|
|
myDebug( << "write failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
|
error(ERR_CONNECTION_BROKEN,connectionHost);
|
|
|
shutdownConnection();
|
|
|
return;
|
|
|
}
|
|
|
if (outBufPos >= outBufLen) {
|
|
|
outBufPos = -1;
|
|
|
outBuf = NULL;
|
|
|
sent();
|
|
|
}
|
|
|
}
|
|
|
else if (FD_ISSET(childFd,&rfds)) {
|
|
|
rc = read(childFd,buf+offset,32768-offset);
|
|
|
//myDebug( << "read " << rc << " bytes" << endl);
|
|
|
if (rc > 0) {
|
|
|
int noff = received(buf,rc+offset);
|
|
|
if (noff > 0) memmove(buf,buf+offset+rc-noff,noff);
|
|
|
//myDebug( << "left " << noff << " bytes: " << QString::fromLatin1(buf,offset) << endl);
|
|
|
offset = noff;
|
|
|
} else {
|
|
|
if (errno == EINTR)
|
|
|
continue;
|
|
|
myDebug( << "read failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
|
error(ERR_CONNECTION_BROKEN,connectionHost);
|
|
|
shutdownConnection();
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
if (wasKilled())
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
/** stat a file */
|
|
|
void fishProtocol::stat(const KURL& u){
|
|
|
myDebug( << "@@@@@@@@@ stat " << u << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
isStat = true; // FIXME: just a workaround for konq deficiencies
|
|
|
openConnection();
|
|
|
isStat = false; // FIXME: just a workaround for konq deficiencies
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
sendCommand(FISH_STAT,E(url.path(-1)));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** find mimetype for a file */
|
|
|
void fishProtocol::mimetype(const KURL& u){
|
|
|
myDebug( << "@@@@@@@@@ mimetype " << u << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
recvLen = 1024;
|
|
|
sendCommand(FISH_READ,"0","1024",E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** list a directory */
|
|
|
void fishProtocol::listDir(const KURL& u){
|
|
|
myDebug( << "@@@@@@@@@ listDir " << u << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
listReason = LIST;
|
|
|
sendCommand(FISH_LIST,E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** create a directory */
|
|
|
void fishProtocol::mkdir(const KURL& u, int permissions) {
|
|
|
myDebug( << "@@@@@@@@@ mkdir " << u << " " << permissions << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
sendCommand(FISH_MKD,E(url.path()));
|
|
|
if (permissions > -1) sendCommand(FISH_CHMOD,E(QString::number(permissions,8)),E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** rename a file */
|
|
|
void fishProtocol::rename(const KURL& s, const KURL& d, bool overwrite) {
|
|
|
myDebug( << "@@@@@@@@@ rename " << s << " " << d << " " << overwrite << endl);
|
|
|
if (s.host() != d.host() || s.port() != d.port() || s.user() != d.user()) {
|
|
|
error(ERR_UNSUPPORTED_ACTION,s.prettyURL());
|
|
|
return;
|
|
|
}
|
|
|
setHost(s.host(),s.port(),s.user(),s.pass());
|
|
|
url = d;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
KURL src = s;
|
|
|
url.cleanPath();
|
|
|
src.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
if (!overwrite) {
|
|
|
listReason = CHECK;
|
|
|
checkOverwrite = false;
|
|
|
sendCommand(FISH_LIST,E(url.path()));
|
|
|
}
|
|
|
sendCommand(FISH_RENAME,E(src.path()),E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** create a symlink */
|
|
|
void fishProtocol::symlink(const QString& target, const KURL& u, bool overwrite) {
|
|
|
myDebug( << "@@@@@@@@@ symlink " << target << " " << u << " " << overwrite << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
if (!overwrite) {
|
|
|
listReason = CHECK;
|
|
|
checkOverwrite = false;
|
|
|
sendCommand(FISH_LIST,E(url.path()));
|
|
|
}
|
|
|
sendCommand(FISH_SYMLINK,E(target),E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** change file permissions */
|
|
|
void fishProtocol::chmod(const KURL& u, int permissions){
|
|
|
myDebug( << "@@@@@@@@@ chmod " << u << " " << permissions << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
if (permissions > -1) sendCommand(FISH_CHMOD,E(QString::number(permissions,8)),E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** copies a file */
|
|
|
void fishProtocol::copy(const KURL &s, const KURL &d, int permissions, bool overwrite) {
|
|
|
myDebug( << "@@@@@@@@@ copy " << s << " " << d << " " << permissions << " " << overwrite << endl);
|
|
|
if (s.host() != d.host() || s.port() != d.port() || s.user() != d.user()) {
|
|
|
error(ERR_UNSUPPORTED_ACTION,s.prettyURL());
|
|
|
return;
|
|
|
}
|
|
|
//myDebug( << s << endl << d << endl);
|
|
|
setHost(s.host(),s.port(),s.user(),s.pass());
|
|
|
url = d;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
KURL src = s;
|
|
|
url.cleanPath();
|
|
|
src.cleanPath();
|
|
|
if (!src.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
if (!overwrite) {
|
|
|
listReason = CHECK;
|
|
|
checkOverwrite = false;
|
|
|
sendCommand(FISH_LIST,E(url.path()));
|
|
|
}
|
|
|
sendCommand(FISH_COPY,E(src.path()),E(url.path()));
|
|
|
if (permissions > -1) sendCommand(FISH_CHMOD,E(QString::number(permissions,8)),E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** removes a file or directory */
|
|
|
void fishProtocol::del(const KURL &u, bool isFile){
|
|
|
myDebug( << "@@@@@@@@@ del " << u << " " << isFile << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
url.cleanPath();
|
|
|
if (!url.hasPath()) {
|
|
|
sendCommand(FISH_PWD);
|
|
|
} else {
|
|
|
sendCommand((isFile?FISH_DELE:FISH_RMD),E(url.path()));
|
|
|
}
|
|
|
run();
|
|
|
}
|
|
|
/** special like background execute */
|
|
|
void fishProtocol::special( const QByteArray &data ){
|
|
|
int tmp;
|
|
|
|
|
|
QDataStream stream(data, IO_ReadOnly);
|
|
|
|
|
|
stream >> tmp;
|
|
|
switch (tmp) {
|
|
|
case FISH_EXEC_CMD: // SSH EXEC
|
|
|
{
|
|
|
KURL u;
|
|
|
QString command;
|
|
|
QString tempfile;
|
|
|
stream >> u;
|
|
|
stream >> command;
|
|
|
myDebug( << "@@@@@@@@@ exec " << u << " " << command << endl);
|
|
|
setHost(u.host(),u.port(),u.user(),u.pass());
|
|
|
url = u;
|
|
|
openConnection();
|
|
|
if (!isLoggedIn) return;
|
|
|
sendCommand(FISH_EXEC,E(command),E(url.path()));
|
|
|
run();
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
// Some command we don't understand.
|
|
|
error(ERR_UNSUPPORTED_ACTION,QString().setNum(tmp));
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
/** report status */
|
|
|
void fishProtocol::slave_status() {
|
|
|
myDebug( << "@@@@@@@@@ slave_status" << endl);
|
|
|
if (childPid > 0)
|
|
|
slaveStatus(connectionHost,isLoggedIn);
|
|
|
else
|
|
|
slaveStatus(QString::null,false);
|
|
|
}
|