You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1116 lines
35 KiB
1116 lines
35 KiB
/*
|
|
Copyright (C) 2000, 2001, 2002 Dawit Alemayehu <adawit@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#define HAVE_TERMIOS_H 1
|
|
#define HAVE_GRANTPT 1
|
|
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_PTY_H
|
|
#include <pty.h>
|
|
#endif
|
|
#ifdef HAVE_TERMIOS_H
|
|
#include <termios.h>
|
|
#endif
|
|
#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 <qfile.h>
|
|
#include <qtimer.h>
|
|
#include <qapplication.h>
|
|
#include <qlabel.h>
|
|
#include <qpushbutton.h>
|
|
#include <qhbox.h>
|
|
#include <qwhatsthis.h>
|
|
#include <qiconview.h>
|
|
#include <qpainter.h>
|
|
#include <qpixmap.h>
|
|
#include <qlabel.h>
|
|
#include <qlayout.h>
|
|
#include <qpushbutton.h>
|
|
#include <qstring.h>
|
|
#include <qregexp.h>
|
|
#include <qstyle.h>
|
|
#include <qtimer.h>
|
|
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <kinstance.h>
|
|
|
|
#include <kwin.h>
|
|
#include <kurl.h>
|
|
#include <kaction.h>
|
|
#include <kpopupmenu.h>
|
|
#include <kmessagebox.h>
|
|
#include <kiconloader.h>
|
|
#include <kprogressbox.h>
|
|
#include <kpassdlg.h>
|
|
#include <klistview.h>
|
|
#include <kapplication.h>
|
|
#include <kconfigdialog.h>
|
|
|
|
#include <kdirlister.h>
|
|
#include <kstandarddirs.h>
|
|
#include <klistviewsearchline.h>
|
|
#include <kiconviewsearchline.h>
|
|
#include <konq_dirpart.h>
|
|
#include <konq_propsview.h>
|
|
#include <kstaticdeleter.h>
|
|
#include <kgenericfactory.h>
|
|
#include <kparts/browserextension.h>
|
|
|
|
#include <kio/global.h>
|
|
#include <kio/slavebase.h>
|
|
|
|
#include "rsyncplugin.h"
|
|
#include "rsyncconfigdialog.h"
|
|
|
|
#define myDebug(x) kdDebug(7127) << __LINE__ << ": " x
|
|
|
|
typedef KGenericFactory<RsyncPlugin> RsyncPluginFactory;
|
|
K_EXPORT_COMPONENT_FACTORY(librsyncplugin,
|
|
RsyncPluginFactory("rsyncplugin"))
|
|
|
|
RsyncPlugin::RsyncPlugin (QObject* parent, const char* name,
|
|
const QStringList&)
|
|
:KParts::Plugin (parent, name),
|
|
m_pSyncNow(0),
|
|
m_pSyncSetup(0)
|
|
{
|
|
m_part = ::qt_cast<KonqDirPart*>(parent);
|
|
|
|
if ( !m_part || !m_part->scrollWidget() )
|
|
return;
|
|
|
|
m_pSyncNow = new KAction(i18n("Synchronize F&older"), "syncnow",
|
|
actionCollection(), "syncnow");
|
|
m_pSyncSetup = new KAction (i18n("Setup Syn&chronization"), "setupsync",
|
|
actionCollection(), "setupsync");
|
|
m_pSyncNow->setIcon("remotesync");
|
|
m_pSyncSetup->setIcon("remotesyncconfig");
|
|
m_pSyncNow->setEnabled (false);
|
|
|
|
connect (m_part, SIGNAL(aboutToOpenURL()), SLOT(slotOpenURL()));
|
|
|
|
connect(m_pSyncNow, SIGNAL(activated()), this, SLOT(slotSync()));
|
|
connect(m_pSyncSetup, SIGNAL(activated()), this, SLOT(slotSetup()));
|
|
|
|
loadSettings();
|
|
|
|
childPid = 0;
|
|
isLoggedIn = false;
|
|
firstLogin = true;
|
|
connectionAuth.keepPassword = true;
|
|
outBufPos = -1;
|
|
outBuf = NULL;
|
|
outBufLen = 0;
|
|
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
|
|
}
|
|
|
|
RsyncPlugin::~RsyncPlugin()
|
|
{
|
|
delete m_pSyncNow;
|
|
delete m_pSyncSetup;
|
|
}
|
|
|
|
static char *rsyncPath = NULL;
|
|
static char *suPath = NULL;
|
|
|
|
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 unidirectional sync subprocess
|
|
*/
|
|
bool RsyncPlugin::syncUnidirectional(QString synccommand, QString syncflags, int parameter_order, QString localfolder, QString remotepath) {
|
|
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;
|
|
}
|
|
|
|
childPid = fork();
|
|
if (childPid == -1) {
|
|
myDebug( << "fork failed, error: " << strerror(errno) << endl);
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
childPid = 0;
|
|
return true;
|
|
}
|
|
if (childPid == 0) {
|
|
// Create the rsync command to run
|
|
QString execstring;
|
|
if (parameter_order == 0) {
|
|
execstring = synccommand + syncflags + localfolder + QString("/ ") + remotepath;
|
|
}
|
|
else {
|
|
execstring = synccommand + syncflags + remotepath + QString("/ ") + localfolder;
|
|
}
|
|
|
|
// 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);
|
|
|
|
system(execstring.ascii());
|
|
#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);
|
|
fflush(stdout);
|
|
}
|
|
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 = establishConnectionRsync(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;
|
|
//if (errno == EAGAIN)
|
|
// continue;
|
|
myDebug( << "read failed, rc: " << rc << ", error: " << strerror(errno) << endl);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
creates the bidirectional sync subprocess
|
|
*/
|
|
bool RsyncPlugin::syncBidirectional(QString synccommand, QString syncflags, int parameter_order, QString localfolder, QString remotepath) {
|
|
int fd[2];
|
|
int rc, flags;
|
|
thisFn = QString::null;
|
|
|
|
// Check for and remove the trailing slash in localfolder
|
|
if (localfolder.endsWith("/")) {
|
|
localfolder.remove(localfolder.length()-1, 1);
|
|
}
|
|
|
|
rc = open_pty_pair(fd);
|
|
if (rc == -1) {
|
|
myDebug( << "socketpair failed, error: " << strerror(errno) << endl);
|
|
return true;
|
|
}
|
|
|
|
childPid = fork();
|
|
if (childPid == -1) {
|
|
myDebug( << "fork failed, error: " << strerror(errno) << endl);
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
childPid = 0;
|
|
return true;
|
|
}
|
|
if (childPid == 0) {
|
|
// Create the rsync command to run
|
|
QString execstring;
|
|
execstring = synccommand + syncflags + localfolder + QString(" ") + remotepath;
|
|
|
|
// 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);
|
|
|
|
system(execstring.ascii());
|
|
#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);
|
|
fflush(stdout);
|
|
}
|
|
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 = establishConnectionUnison(buf,rc+offset, localfolder, remotepath);
|
|
if (noff < 0) return false;
|
|
if (noff > 0) memmove(buf,buf+offset+rc-noff,noff);
|
|
offset = noff;
|
|
} else {
|
|
if (errno == EINTR)
|
|
continue;
|
|
//if (errno == EAGAIN)
|
|
// 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 RsyncPlugin::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 RsyncPlugin::establishConnectionRsync(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("?"))) {
|
|
if (m_progressDialogExists == true) {
|
|
qApp->processEvents();
|
|
}
|
|
pos++;
|
|
QString str = buf.left(pos);
|
|
buf = buf.mid(pos);
|
|
if (str == "\n")
|
|
continue;
|
|
//if (str.contains("rsync error:")) {
|
|
if (str.contains("rsync:") || str.contains("failed.") || (str.contains("Could not") && str.endsWith("."))) {
|
|
KMessageBox::error(NULL, str);
|
|
}
|
|
else if (!str.isEmpty()) {
|
|
thisFn += str;
|
|
if ((buf.endsWith(":") == false) && (buf.endsWith("?") == false)) {
|
|
// Display a nice little progress bar with text box
|
|
if (m_progressDialogExists == false) {
|
|
m_progressDialog = new KProgressBoxDialog(0, "rsyncProgress", i18n("Synchronizing Folder..."), i18n("Synchronizing Folder..."), true);
|
|
m_progressDialog->progressBar()->setFormat("%v / %m");
|
|
m_progressDialog->setAutoClose(true);
|
|
m_progressDialog->progressBar()->setTotalSteps(2);
|
|
m_progressDialog->progressBar()->setValue(1);
|
|
connect (m_progressDialog, SIGNAL(cancelClicked()), SLOT(slotRsyncCancelled()));
|
|
m_progressDialog->show();
|
|
m_progressDialogExists = true;
|
|
}
|
|
}
|
|
}
|
|
else if (buf.endsWith(":")) {
|
|
if (!redirectUser.isEmpty() && connectionUser != redirectUser) {
|
|
// FIXME: Possibly do something here; is this the success response?
|
|
return -1;
|
|
} else if (!connectionPassword.isEmpty()) {
|
|
myDebug( << "sending cpass" << endl);
|
|
connectionAuth.password = connectionPassword+"\n";
|
|
connectionPassword = QString::null;
|
|
writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
|
|
} else {
|
|
myDebug( << "sending mpass" << endl);
|
|
connectionAuth.prompt = thisFn+buf;
|
|
connectionAuth.password = QString::null; // don't prefill
|
|
QCString thispass;
|
|
if (KPasswordDialog::getPassword (thispass, i18n("Remote authorization required") + QString("\n") + i18n("Please input") + QString(" ") + QString(buf), NULL) != 1) {
|
|
shutdownConnection(true, false);
|
|
return -1;
|
|
}
|
|
else {
|
|
connectionAuth.password = QString(thispass);
|
|
}
|
|
connectionAuth.password += "\n";
|
|
myDebug( << "sending pass" << endl);
|
|
writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
|
|
}
|
|
thisFn = QString::null;
|
|
return 0;
|
|
}
|
|
else if (buf.endsWith("?")) {
|
|
int rc = KMessageBox::questionYesNo(NULL, thisFn+buf);
|
|
if (rc == KMessageBox::Yes) {
|
|
writeChild("yes\n",4);
|
|
} else {
|
|
writeChild("no\n",3);
|
|
}
|
|
thisFn = QString::null;
|
|
return 0;
|
|
}
|
|
|
|
if (m_progressDialogExists == true) {
|
|
if (str.contains("exit()") && str.contains("ICE default IO")) {
|
|
if (m_progressDialogExists == true) {
|
|
m_progressDialog->progressBar()->setValue(m_progressDialog->progressBar()->totalSteps());
|
|
}
|
|
}
|
|
else {
|
|
if (str.contains(", to-check=")) {
|
|
// Parse the to-check output
|
|
QString tocheck_out_cur;
|
|
QString tocheck_out_tot;
|
|
tocheck_out_cur = str.mid(str.find(", to-check=") + 11, str.length());
|
|
tocheck_out_tot = tocheck_out_cur.mid(tocheck_out_cur.find("/") + 1, tocheck_out_cur.length());
|
|
tocheck_out_cur = tocheck_out_cur.left(tocheck_out_cur.find("/"));
|
|
tocheck_out_tot = tocheck_out_tot.left(tocheck_out_tot.find(")"));
|
|
m_progressDialog->progressBar()->setTotalSteps(tocheck_out_tot.toInt()-1);
|
|
m_progressDialog->progressBar()->setValue(tocheck_out_tot.toInt()-tocheck_out_cur.toInt()-2);
|
|
}
|
|
else {
|
|
m_progressDialog->textEdit()->append(str);
|
|
m_progressDialog->textEdit()->scrollToBottom();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return buf.length();
|
|
}
|
|
|
|
/**
|
|
manages initial communication setup including password queries
|
|
*/
|
|
int RsyncPlugin::establishConnectionUnison(char *buffer, KIO::fileoffset_t len, QString localfolder, QString remotepath) {
|
|
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("?") || buf.endsWith("]"))) {
|
|
if (m_progressDialogExists == true) {
|
|
qApp->processEvents();
|
|
}
|
|
pos++;
|
|
QString str = buf.left(pos);
|
|
buf = buf.mid(pos);
|
|
if (str == "\n")
|
|
continue;
|
|
//if (str.contains("rsync error:")) {
|
|
if (str.contains("rsync:") || str.contains("failed.") || (str.contains("Could not") && str.endsWith("."))) {
|
|
KMessageBox::error(NULL, str);
|
|
}
|
|
else if (!str.isEmpty()) {
|
|
thisFn += str;
|
|
if ((buf.endsWith(":") == false) && (buf.endsWith("?") == false)) {
|
|
// Display a nice little progress bar with text box
|
|
if (m_progressDialogExists == false) {
|
|
m_progressDialog = new KProgressBoxDialog(0, "rsyncProgress", i18n("Synchronizing Folder..."), i18n("Synchronizing Folder..."), true);
|
|
m_progressDialog->progressBar()->setFormat("%v / %m");
|
|
m_progressDialog->progressBar()->setTotalSteps(0);
|
|
m_progressDialog->setAutoClose(true);
|
|
connect (m_progressDialog, SIGNAL(cancelClicked()), SLOT(slotUnisonCancelled()));
|
|
m_progressDialog->show();
|
|
m_progressDialogExists = true;
|
|
}
|
|
}
|
|
}
|
|
else if (buf.endsWith(":")) {
|
|
if (!redirectUser.isEmpty() && connectionUser != redirectUser) {
|
|
// FIXME: Possibly do something here; is this the success response?
|
|
return -1;
|
|
} else if (!connectionPassword.isEmpty()) {
|
|
myDebug( << "sending cpass" << endl);
|
|
connectionAuth.password = connectionPassword+"\n";
|
|
connectionPassword = QString::null;
|
|
writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
|
|
} else {
|
|
myDebug( << "sending mpass" << endl);
|
|
connectionAuth.prompt = thisFn+buf;
|
|
connectionAuth.password = QString::null; // don't prefill
|
|
QCString thispass;
|
|
if (KPasswordDialog::getPassword (thispass, i18n("Remote authorization required") + QString("\n") + i18n("Please input") + QString(" ") + QString(buf), NULL) != 1) {
|
|
slotUnisonCancelled();
|
|
return -1;
|
|
}
|
|
else {
|
|
connectionAuth.password = QString(thispass);
|
|
}
|
|
connectionAuth.password += "\n";
|
|
myDebug( << "sending pass" << endl);
|
|
writeChild(connectionAuth.password.latin1(),connectionAuth.password.length());
|
|
}
|
|
thisFn = QString::null;
|
|
return 0;
|
|
}
|
|
else if (buf.endsWith("?") || buf.endsWith("? []")) {
|
|
buf.replace("[]", "");
|
|
if (buf.endsWith("? []")) {
|
|
int rc = KMessageBox::questionYesNo(NULL, buf);
|
|
if (rc == KMessageBox::Yes) {
|
|
writeChild("y\n",3);
|
|
} else {
|
|
writeChild("n\n",3);
|
|
}
|
|
}
|
|
else {
|
|
int rc = KMessageBox::questionYesNo(NULL, buf);
|
|
if (rc == KMessageBox::Yes) {
|
|
writeChild("yes\n",4);
|
|
} else {
|
|
writeChild("no\n",3);
|
|
}
|
|
}
|
|
thisFn = QString::null;
|
|
buf = "";
|
|
return 0;
|
|
}
|
|
else if (buf.endsWith("]")) {
|
|
if (m_progressDialogExists == true) {
|
|
m_progressDialog->textEdit()->append(buf);
|
|
m_progressDialog->textEdit()->scrollToBottom();
|
|
int currentPos;
|
|
currentPos = m_progressDialog->progressBar()->progress();
|
|
m_progressDialog->progressBar()->setProgress(++currentPos);
|
|
}
|
|
QString file_name;
|
|
file_name = buf;
|
|
file_name.replace("[]", "");
|
|
file_name.replace(QString("changed "), "");
|
|
//file_name = file_name.simplifyWhiteSpace();
|
|
KDialogBase *dialog= new KDialogBase(i18n("User Intervention Required"), KDialogBase::Yes | KDialogBase::No | KDialogBase::Cancel, KDialogBase::Yes, KDialogBase::Cancel, NULL, "warningYesNoCancel", true, true, i18n("Use &Local File"), i18n("Use &Remote File"), i18n("&Ignore"));
|
|
int rc = KMessageBox::createKMessageBox(dialog, QMessageBox::Warning, QString("<b>") + i18n("WARNING: Both the local and remote file have been modified") + QString("</b><p>") + i18n("Local") + QString(": ") + localfolder + QString("/") + file_name + QString("<br>") + i18n("Remote") + QString(": ") + remotepath + QString("/") + file_name + QString("<p>") + i18n("Please select the file to duplicate (the other will be overwritten)") + QString("<br>") + i18n("Or, select Ignore to skip synchronization of this file for now"), QStringList(), QString::null, NULL, 1);
|
|
if (rc == KDialogBase::Yes) {
|
|
writeChild(">\n",3);
|
|
}
|
|
else if (rc == KDialogBase::No) {
|
|
writeChild("<\n",3);
|
|
}
|
|
else {
|
|
writeChild("/\n",3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (m_progressDialogExists == true) {
|
|
if (str.contains("exit()") && str.contains("ICE default IO")) {
|
|
if (m_progressDialogExists == true) {
|
|
m_progressDialog->progressBar()->setFormat("%v / %m");
|
|
m_progressDialog->progressBar()->setTotalSteps(2);
|
|
m_progressDialog->progressBar()->setValue(m_progressDialog->progressBar()->totalSteps());
|
|
}
|
|
}
|
|
else {
|
|
m_progressDialog->textEdit()->append(str);
|
|
m_progressDialog->textEdit()->scrollToBottom();
|
|
int currentPos;
|
|
currentPos = m_progressDialog->progressBar()->progress();
|
|
m_progressDialog->progressBar()->setProgress(++currentPos);
|
|
}
|
|
}
|
|
}
|
|
return buf.length();
|
|
}
|
|
|
|
/**
|
|
Forced close of the connection
|
|
|
|
This function gets called from the application side of the universe,
|
|
it shouldn't send any response.
|
|
*/
|
|
void RsyncPlugin::closeConnection(){
|
|
myDebug( << "closeConnection()" << endl);
|
|
shutdownConnection(true, false);
|
|
}
|
|
|
|
/**
|
|
Closes the connection
|
|
*/
|
|
void RsyncPlugin::shutdownConnection(bool forced, bool wait){
|
|
if (childPid) {
|
|
kill(childPid,SIGTERM); // We may not have permission...
|
|
childPid = 0;
|
|
if (wait == false) {
|
|
close(childFd); // ...in which case this should do the trick
|
|
childFd = -1;
|
|
}
|
|
}
|
|
outBufPos = -1;
|
|
outBuf = NULL;
|
|
outBufLen = 0;
|
|
isLoggedIn = false;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
//
|
|
// Here begins the standard load/save/search/Konqy stuff
|
|
//
|
|
// --------------------------------------------------------------------------------------------
|
|
|
|
void RsyncPlugin::saveSettings()
|
|
{
|
|
KConfig cfg ("rsyncrc", false, false);
|
|
cfg.setGroup ("General");
|
|
|
|
QString longstring = QString("");
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end();) {
|
|
longstring = longstring + (*i);
|
|
i++;
|
|
longstring = longstring + QString(";");
|
|
}
|
|
|
|
cfg.writeEntry("LocalFolders", longstring);
|
|
cfg.sync();
|
|
}
|
|
|
|
void RsyncPlugin::loadSettings()
|
|
{
|
|
if (m_bSettingsLoaded)
|
|
return;
|
|
|
|
KConfig cfg ("rsyncrc", false, false);
|
|
cfg.setGroup ("General");
|
|
|
|
cfgfolderlist = cfg.readListEntry("LocalFolders", ';');
|
|
|
|
m_bSettingsLoaded = true;
|
|
}
|
|
|
|
QString RsyncPlugin::findLocalFolderByName(QString folderurl)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end(); ++i) {
|
|
if (QString::compare((*i), folderurl_stripped) == 0) {
|
|
i++;
|
|
return (*i);
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
QString RsyncPlugin::findSyncMethodByName(QString folderurl)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end(); ++i) {
|
|
if (QString::compare((*i), folderurl_stripped) == 0) {
|
|
i++;
|
|
i++;
|
|
return (*i);
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
QString RsyncPlugin::findLoginSyncEnabledByName(QString folderurl)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end(); ++i) {
|
|
if (QString::compare((*i), folderurl_stripped) == 0) {
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
return (*i);
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
QString RsyncPlugin::findLogoutSyncEnabledByName(QString folderurl)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end(); ++i) {
|
|
if (QString::compare((*i), folderurl_stripped) == 0) {
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
return (*i);
|
|
i++;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
QString RsyncPlugin::findTimedSyncEnabledByName(QString folderurl)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end(); ++i) {
|
|
if (QString::compare((*i), folderurl_stripped) == 0) {
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
i++;
|
|
return (*i);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int RsyncPlugin::deleteLocalFolderByName(QString folderurl)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
for (QStringList::Iterator i(cfgfolderlist.begin()); i != cfgfolderlist.end(); ++i) {
|
|
if (QString::compare((*i), folderurl_stripped) == 0) {
|
|
i=cfgfolderlist.remove(i);
|
|
i=cfgfolderlist.remove(i);
|
|
i=cfgfolderlist.remove(i);
|
|
i=cfgfolderlist.remove(i);
|
|
i=cfgfolderlist.remove(i);
|
|
i=cfgfolderlist.remove(i);
|
|
cfgfolderlist.remove(i);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int RsyncPlugin::addLocalFolderByName(QString folderurl, QString remoteurl, QString syncmethod, QString excludelist, QString sync_on_login, QString sync_on_logout, QString sync_timed_interval)
|
|
{
|
|
QString folderurl_stripped;
|
|
folderurl_stripped = folderurl;
|
|
folderurl_stripped.replace(QString("file://"), QString(""));
|
|
cfgfolderlist.append(folderurl);
|
|
cfgfolderlist.append(remoteurl);
|
|
cfgfolderlist.append(syncmethod);
|
|
cfgfolderlist.append(excludelist);
|
|
cfgfolderlist.append(sync_on_login);
|
|
cfgfolderlist.append(sync_on_logout);
|
|
cfgfolderlist.append(sync_timed_interval);
|
|
return 1;
|
|
}
|
|
|
|
void RsyncPlugin::slotOpenURL ()
|
|
{
|
|
KURL url = m_part->url();
|
|
|
|
if (m_pURL != url)
|
|
{
|
|
// See if this URL is "/", "/dev", or "/proc", and disable sync if so
|
|
// Also disable sync for non-"file://" URLs
|
|
if ((url.directory(true, true) + QString("/") + url.fileName(true)) == "//") {
|
|
m_pSyncSetup->setEnabled(false);
|
|
m_pSyncNow->setEnabled(false);
|
|
}
|
|
else if (((url.directory(true, true) + QString("/") + url.fileName(true)).left(5) == "//dev") || ((url.directory(true, true) + QString("/") + url.fileName(true)).left(4) == "/dev")) {
|
|
m_pSyncSetup->setEnabled(false);
|
|
m_pSyncNow->setEnabled(false);
|
|
}
|
|
else if (((url.directory(true, true) + QString("/") + url.fileName(true)).left(6) == "//proc") || ((url.directory(true, true) + QString("/") + url.fileName(true)).left(5) == "/proc")) {
|
|
m_pSyncSetup->setEnabled(false);
|
|
m_pSyncNow->setEnabled(false);
|
|
}
|
|
else if (url.protocol() != QString("file")) {
|
|
m_pSyncSetup->setEnabled(false);
|
|
m_pSyncNow->setEnabled(false);
|
|
}
|
|
else {
|
|
m_pSyncSetup->setEnabled(true);
|
|
|
|
// See if this URL is in the list of rsync-able directories
|
|
if (findLocalFolderByName(url.directory(true, true) + QString("/") + url.fileName(true)) != NULL) {
|
|
m_pSyncNow->setEnabled (true);
|
|
}
|
|
else {
|
|
m_pSyncNow->setEnabled (false);
|
|
}
|
|
}
|
|
}
|
|
m_pURL = url;
|
|
}
|
|
|
|
void RsyncPlugin::slotSetup()
|
|
{
|
|
KURL url = m_part->url();
|
|
|
|
m_pSyncSetup->setEnabled (false);
|
|
|
|
// Look up settings
|
|
QString localfolder = url.directory(true, true) + QString("/") + url.fileName(true);
|
|
QString remotefolder = findLocalFolderByName(url.directory(true, true) + QString("/") + url.fileName(true));
|
|
QString syncmethod = findSyncMethodByName(url.directory(true, true) + QString("/") + url.fileName(true));
|
|
int syncint;
|
|
if (syncmethod == NULL) {
|
|
syncint = 1;
|
|
}
|
|
else if (syncmethod == "rsync_upload") {
|
|
syncint = 1;
|
|
}
|
|
else if (syncmethod == "rsync_download") {
|
|
syncint = 2;
|
|
}
|
|
else if (syncmethod == "rsync_bidirectional") {
|
|
syncint = 3;
|
|
}
|
|
|
|
m_configDialog = new RsyncConfigDialog(0, "rsyncConfig", i18n("Remote Folder Synchronization"), i18n("Configuring Remote Folder Synchronization"), localfolder, remotefolder, syncint, true);
|
|
m_configDialog->show();
|
|
|
|
connect (m_configDialog, SIGNAL(okClicked()), SLOT(slotSetupOK()));
|
|
connect (m_configDialog, SIGNAL(cancelClicked()), SLOT(slotSetupCancelled()));
|
|
}
|
|
|
|
void RsyncPlugin::slotSetupOK()
|
|
{
|
|
if (!m_part)
|
|
return;
|
|
|
|
KURL url = m_part->url();
|
|
|
|
// Look up settings
|
|
QString localfolder = url.directory(true, true) + QString("/") + url.fileName(true);
|
|
QString remotefolder = findLocalFolderByName(localfolder);
|
|
QString remotefolder_new = m_configDialog->lineEdit()->text().ascii();
|
|
int syncmethod = m_configDialog->getSyncMode();
|
|
QString syncmethod_new = "";
|
|
if (syncmethod == 1) {
|
|
syncmethod_new = "rsync_upload";
|
|
}
|
|
else if (syncmethod == 2) {
|
|
syncmethod_new = "rsync_download";
|
|
}
|
|
else if (syncmethod == 3) {
|
|
syncmethod_new = "rsync_bidirectional";
|
|
}
|
|
|
|
// See if an old entry has to be deleted
|
|
if (remotefolder.isEmpty() == false) {
|
|
deleteLocalFolderByName(localfolder);
|
|
}
|
|
if (remotefolder_new.isEmpty() == false) {
|
|
addLocalFolderByName(localfolder, remotefolder_new, syncmethod_new, "", "0", "0", "-1");
|
|
}
|
|
saveSettings();
|
|
|
|
if (remotefolder_new.isEmpty() == false) {
|
|
m_pSyncNow->setEnabled (true);
|
|
}
|
|
else {
|
|
m_pSyncNow->setEnabled (false);
|
|
}
|
|
m_pSyncSetup->setEnabled (true);
|
|
}
|
|
|
|
void RsyncPlugin::slotSetupCancelled()
|
|
{
|
|
m_pSyncSetup->setEnabled (true);
|
|
}
|
|
|
|
void RsyncPlugin::slotRsyncCancelled()
|
|
{
|
|
shutdownConnection(true, true);
|
|
if (m_progressDialogExists == true) {
|
|
m_progressDialog->progressBar()->setValue(m_progressDialog->progressBar()->totalSteps());
|
|
}
|
|
m_pSyncNow->setEnabled (true);
|
|
}
|
|
|
|
void RsyncPlugin::slotUnisonCancelled()
|
|
{
|
|
shutdownConnection(true, true);
|
|
if (m_progressDialogExists == true) {
|
|
m_progressDialog->progressBar()->setFormat("%v / %m");
|
|
m_progressDialog->progressBar()->setTotalSteps(2);
|
|
m_progressDialog->progressBar()->setValue(m_progressDialog->progressBar()->totalSteps());
|
|
}
|
|
m_pSyncNow->setEnabled (true);
|
|
}
|
|
|
|
void RsyncPlugin::slotSync()
|
|
{
|
|
if (!m_part)
|
|
return;
|
|
|
|
KURL url = m_part->url();
|
|
|
|
m_pSyncNow->setEnabled (false);
|
|
|
|
QString syncmethod = findSyncMethodByName(url.directory(true, true) + QString("/") + url.fileName(true));
|
|
if (syncmethod == NULL) {
|
|
// Do nothing
|
|
}
|
|
else if (syncmethod == "rsync_upload") {
|
|
// Initiate rsync
|
|
syncUnidirectional(QString("rsync"), QString(" -avtzAXE --delete --progress "), 0, url.directory(true, true) + QString("/") + url.fileName(true), findLocalFolderByName(url.directory(true, true) + QString("/") + url.fileName(true)));
|
|
}
|
|
else if (syncmethod == "rsync_download") {
|
|
syncUnidirectional(QString("rsync"), QString(" -avtzAXE --delete --progress "), 1, url.directory(true, true) + QString("/") + url.fileName(true), findLocalFolderByName(url.directory(true, true) + QString("/") + url.fileName(true)));
|
|
}
|
|
else if (syncmethod == "rsync_bidirectional") {
|
|
syncBidirectional(QString("unison"), QString(" -ui text -auto "), 1, url.directory(true, true) + QString("/") + url.fileName(true), findLocalFolderByName(url.directory(true, true) + QString("/") + url.fileName(true)));
|
|
}
|
|
|
|
m_progressDialogExists = false;
|
|
m_pSyncNow->setEnabled (true);
|
|
}
|
|
|
|
#include "rsyncplugin.moc"
|