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.
kgtk-qt3/kdialogd4/kdialogd.cpp

760 lines
20 KiB

#define USE_KWIN
#include "kdialogd.h"
#include <iostream>
#include <kdiroperator.h>
#include <kuniqueapplication.h>
#include <QtCore/QSocketNotifier>
#include <QtGui/QX11Info>
#include <kio/netaccess.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kconfig.h>
#include <kurlcombobox.h>
#ifdef USE_KWIN
#include <kwindowsystem.h>
#else
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <fixx11h.h>
#endif
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
#include <kdebug.h>
#include <kdeversion.h>
#ifdef KDIALOGD_APP
#include <QtCore/QTimer>
#include <kcmdlineargs.h>
#include <kaboutdata.h>
#endif
#include <kabstractfilewidget.h>
#include <fstream>
KConfig *KDialogD::theirConfig=NULL;
#define CFG_KEY_DIALOG_SIZE "KDialogDSize"
#define CFG_TIMEOUT_GROUP "General"
#ifdef KDIALOGD_APP
#define CFG_TIMEOUT_KEY "Timeout"
#define DEFAULT_TIMEOUT 30
#endif
static QString groupName(const QString &app, bool fileDialog=true)
{
return QString(fileDialog ? "KFileDialog " : "KDirSelectDialog ")+app;
}
// from kdebase/kdesu
typedef unsigned ksocklen_t;
static int createSocket()
{
int socketFd;
ksocklen_t addrlen;
struct stat s;
const char *sock=getSockName();
int stat_err=lstat(sock, &s);
if(!stat_err && S_ISLNK(s.st_mode))
{
kWarning() << "Someone is running a symlink attack on you" ;
if(unlink(sock))
{
kWarning() << "Could not delete symlink" ;
return -1;
}
}
if (!access(sock, R_OK|W_OK))
{
kWarning() << "stale socket exists" ;
if (unlink(sock))
{
kWarning() << "Could not delete stale socket" ;
return -1;
}
}
socketFd = socket(PF_UNIX, SOCK_STREAM, 0);
if (socketFd < 0)
{
kError() << "socket(): " << strerror(errno);
return -1;
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock, sizeof(addr.sun_path)-1);
addr.sun_path[sizeof(addr.sun_path)-1] = '\000';
addrlen = SUN_LEN(&addr);
if (bind(socketFd, (struct sockaddr *)&addr, addrlen) < 0)
{
kError() << "bind(): " << strerror(errno);
return -1;
}
struct linger lin;
lin.l_onoff = lin.l_linger = 0;
if (setsockopt(socketFd, SOL_SOCKET, SO_LINGER, (char *) &lin,
sizeof(linger)) < 0)
{
kError() << "setsockopt(SO_LINGER): " << strerror(errno);
return -1;
}
int opt = 1;
if (setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt,
sizeof(opt)) < 0)
{
kError() << "setsockopt(SO_REUSEADDR): " << strerror(errno);
return -1;
}
opt = 1;
if (setsockopt(socketFd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
sizeof(opt)) < 0)
{
kError() << "setsockopt(SO_KEEPALIVE): " << strerror(errno);
return -1;
}
chmod(sock, 0600);
if (listen(socketFd, 1) < 0)
{
kError() << "listen(): " << strerror(errno);
return -1;
}
return socketFd;
}
static void urls2Local(KUrl::List &urls, QStringList &items, QWidget *parent)
{
KUrl::List::Iterator it(urls.begin()),
end(urls.end());
for(; it!=end; ++it)
{
kDebug() << "URL:" << *it << " local? " << (*it).isLocalFile();
if((*it).isLocalFile())
items.append((*it).path());
else
{
KUrl url(KIO::NetAccess::mostLocalUrl(*it, parent));
kDebug() << "mostLocal:" << url << " local? " << url.isLocalFile();
if(url.isLocalFile())
items.append(url.path());
else
break;
}
}
}
KDialogD::KDialogD(QObject *parent)
: QObject(parent),
#ifdef KDIALOGD_APP
itsTimer(NULL),
itsTimeoutVal(DEFAULT_TIMEOUT),
#endif
itsFd(::createSocket()),
itsNumConnections(0)
{
if(itsFd<0)
{
kError() << "KDialogD could not create socket";
#ifdef KDIALOGD_APP
kapp->exit();
#endif
}
else
{
std::ofstream f(getPidFileName());
if(f)
{
f << getpid();
f.close();
}
if(!theirConfig)
theirConfig=new KConfig("kdialogd4rc"); // , KConfig::OnlyLocal);
connect(new QSocketNotifier(itsFd, QSocketNotifier::Read, this),
SIGNAL(activated(int)), this, SLOT(newConnection()));
#ifdef KDIALOGD_APP
if(theirConfig->hasGroup(CFG_TIMEOUT_GROUP))
{
itsTimeoutVal=KConfigGroup(theirConfig, CFG_TIMEOUT_GROUP).readEntry(CFG_TIMEOUT_KEY, DEFAULT_TIMEOUT);
if(itsTimeoutVal<0)
itsTimeoutVal=DEFAULT_TIMEOUT;
}
kDebug() << "Timeout:" << itsTimeoutVal;
if(itsTimeoutVal)
{
connect(itsTimer=new QTimer(this), SIGNAL(timeout()), this, SLOT(timeout()));
itsTimer->setSingleShot(true);
}
#endif
}
}
KDialogD::~KDialogD()
{
if(-1!=itsFd)
close(itsFd);
if(theirConfig)
delete theirConfig;
theirConfig=NULL;
}
void KDialogD::newConnection()
{
kDebug() << "New connection";
ksocklen_t addrlen = 64;
struct sockaddr_un clientname;
int connectedFD;
if((connectedFD=::accept(itsFd, (struct sockaddr *) &clientname, &addrlen))>=0)
{
int appNameLen;
if(readBlock(connectedFD, (char *)&appNameLen, 4))
{
bool ok=true;
QByteArray appName;
if(0==appNameLen)
appName="Generic";
else
{
appName.resize(appNameLen);
ok=readBlock(connectedFD, appName.data(), appNameLen);
}
if(ok)
{
itsNumConnections++;
#ifdef KDIALOGD_APP
if(itsTimer)
itsTimer->stop();
#endif
connect(new KDialogDClient(connectedFD, appName, this),
SIGNAL(error(KDialogDClient *)),
this, SLOT(deleteConnection(KDialogDClient *)));
}
}
}
}
void KDialogD::deleteConnection(KDialogDClient *client)
{
kDebug() << "Delete client";
delete client;
#ifdef KDIALOGD_APP
if(0==--itsNumConnections)
if(itsTimeoutVal)
itsTimer->start(itsTimeoutVal*1000); // Only single shot...
else
timeout();
#endif
}
void KDialogD::timeout()
{
#ifdef KDIALOGD_APP
if(0==itsNumConnections)
if(grabLock(0)>0) // 0=> no wait...
{
kDebug() << "Timeout occured, and no connections, so exit";
kapp->exit();
}
else //...unlock lock file...
{
kDebug() << "Timeout occured, but unable to grab lock file - app must be connecting";
releaseLock();
}
#endif
}
KDialogDClient::KDialogDClient(int sock, const QString &an, QObject *parent)
: QObject(parent),
itsFd(sock),
itsDlg(NULL),
itsXid(0),
itsAccepted(false),
itsAppName(an)
{
kDebug() << "new client..." << itsAppName << " (" << itsFd << ")";
connect(new QSocketNotifier(itsFd, QSocketNotifier::Read, this), SIGNAL(activated(int)), this, SLOT(read()));
connect(new QSocketNotifier(itsFd, QSocketNotifier::Exception, this), SIGNAL(activated(int)), this, SLOT(close()));
}
KDialogDClient::~KDialogDClient()
{
kDebug() << "Deleted client";
if(-1!=itsFd)
::close(itsFd);
itsDlg=NULL;
if(KDialogD::config())
KDialogD::config()->sync();
}
void KDialogDClient::close()
{
kDebug() << "close" << itsFd;
::close(itsFd);
itsFd=-1;
if(itsDlg)
{
itsDlg->close();
itsDlg->delayedDestruct();
itsDlg=NULL;
itsXid=0;
}
emit error(this);
}
void KDialogDClient::read()
{
kDebug() << "read" << itsFd;
if(-1==itsFd)
return;
char request;
QString caption;
if(!itsDlg && readData(&request, 1) && request>=(char)OP_FILE_OPEN && request<=(char)OP_FOLDER &&
readData((char *)&itsXid, 4) && readString(caption))
{
if("."==caption)
switch((Operation)request)
{
case OP_FILE_OPEN:
case OP_FILE_OPEN_MULTIPLE:
caption=i18n("Open");
break;
case OP_FILE_SAVE:
caption=i18n("Save As");
break;
case OP_FOLDER:
caption=i18n("Select Folder");
break;
default:
break;
}
if(OP_FOLDER==(Operation)request)
{
QString intialFolder;
if(readString(intialFolder))
{
initDialog(caption, new KDialogDDirSelectDialog(itsAppName, intialFolder, true, NULL, false));
return;
}
}
else
{
QString intialFolder,
filter;
char overW=0;
if(readString(intialFolder) && readString(filter) &&
(OP_FILE_SAVE!=(Operation)request || readData(&overW, 1)))
{
initDialog(caption, new KDialogDFileDialog(itsAppName, (Operation)request, intialFolder,
filter, overW ? true : false));
return;
}
}
}
kDebug() << "Comms error, closing connection..." << itsFd;
// If we get here something was wrong, close connection...
close();
}
void KDialogDClient::finished()
{
if(-1==itsFd)
return;
//
// * finished is emitted when a dialog is ok'ed/cancel'ed/closed
// * if the user just closes the dialog - neither ok nor cancel are emitted
// * the dir select dialog doesnt seem to set the QDialog result parameter
// when it is accepted - so for this reason if ok is clicked we store an
// 'accepted' value there, and check for that after the dialog is finished.
kDebug() << "finished " << (void *)itsDlg << itsAccepted << (itsDlg ? QDialog::Accepted==itsDlg->result() : false);
if(itsDlg && !(itsAccepted || QDialog::Accepted==itsDlg->result()))
cancel();
}
void KDialogDClient::ok(const QStringList &items)
{
kDebug() << "ok";
int num=items.count();
QStringList::ConstIterator it(items.begin()),
end(items.end());
bool error=!writeData((char *)&num, 4);
for(; !error && it!=end; ++it)
{
kDebug() << "writeString " << *it;
error=!writeString(*it);
}
if(error)
close();
else
itsAccepted=true;
if(itsDlg)
itsDlg->delayedDestruct();
itsDlg=NULL;
}
void KDialogDClient::cancel()
{
kDebug() << "cancel";
if(itsDlg)
{
kDebug() << "send cancel";
int rv=0;
if(!writeData((char *)&rv, 4))
{
kDebug() << "failed to write data!";
close();
}
if(itsDlg)
itsDlg->delayedDestruct();
itsDlg=NULL;
}
}
bool KDialogDClient::readData(QByteArray &buffer, int size)
{
kDebug() << "readData" << itsFd;
buffer.resize(size);
return ::readBlock(itsFd, buffer.data(), size);
}
bool KDialogDClient::readString(QString &str)
{
kDebug() << "readString" << itsFd;
int size;
if(!readData((char *)&size, 4))
return false;
QByteArray buffer;
buffer.resize(size);
if(!readData(buffer.data(), size))
return false;
str=QString::fromUtf8(buffer.data());
return true;
}
bool KDialogDClient::writeString(const QString &str)
{
kDebug() << "writeString" << itsFd;
QByteArray utf8(str.toUtf8());
int size=utf8.length()+1;
return writeData((char *)&size, 4) && writeData(utf8.data(), size);
}
void KDialogDClient::initDialog(const QString &caption, KDialog *d)
{
kDebug() << "initDialog" << itsFd;
itsAccepted=false;
itsDlg=d;
if(!caption.isEmpty())
itsDlg->setPlainCaption(caption);
if(itsXid)
itsDlg->installEventFilter(this);
connect(itsDlg, SIGNAL(okClicked()), itsDlg, SLOT(slotOk()));
connect(itsDlg, SIGNAL(ok(const QStringList &)), this, SLOT(ok(const QStringList &)));
connect(itsDlg, SIGNAL(finished()), this, SLOT(finished()));
itsDlg->show();
}
bool KDialogDClient::eventFilter(QObject *object, QEvent *event)
{
if(object==itsDlg && QEvent::ShowToParent==event->type())
{
#ifdef USE_KWIN
KWindowSystem::setMainWindow(itsDlg, itsXid);
KWindowSystem::setState(itsDlg->winId(), NET::Modal|NET::SkipTaskbar|NET::SkipPager);
#if 0
KWindowInfo wi(KWindowSystem::windowInfo(itsXid, NET::WMGeometry, NET::WM2UserTime));
QRect geom(wi.geometry());
int rx=geom.x(),
ry=geom.y();
rx=(rx+(geom.width()/2))-(itsDlg->width()/2);
if(rx<0)
rx=0;
ry=(ry+(geom.height()/2))-(itsDlg->height()/2);
if(ry<0)
ry=0;
itsDlg->move(rx, ry);
#endif
QPixmap icon=KWindowSystem::icon(itsXid, 16, 16, true, KWindowSystem::NETWM | KWindowSystem::WMHints);
if(!icon.isNull())
itsDlg->setWindowIcon(QIcon(icon));
#else
XSetTransientForHint(QX11Info::display(), itsDlg->winId(), itsXid);
#if 0
XWindowAttributes attr;
int rx, ry;
Window junkwin;
if(XGetWindowAttributes(QX11Info::display(), itsXid, &attr))
{
XTranslateCoordinates(QX11Info::display(), itsXid, attr.root,
-attr.border_width, -16,
&rx, &ry, &junkwin);
rx=(rx+(attr.width/2))-(itsDlg->width()/2);
if(rx<0)
rx=0;
ry=(ry+(attr.height/2))-(itsDlg->height()/2);
if(ry<0)
ry=0;
itsDlg->move(rx, ry);
}
#endif
// unsigned long num;
// unsigned long *data = NULL;
// Atom prop = XInternAtom(QX11Info::display(), "_NET_WM_ICON", False);
// Atom typeRet;
// int formatRet;
// unsigned long afterRet;
// if(XGetWindowProperty(QX11Info::display(), itsXid, prop, 0, 0x7FFFFFFF, False, XA_CARDINAL,
// &typeRet, &formatRet, &num, &afterRet, (unsigned char **)&data))
// {
// kDebug() << "GOT ICON!!!";
// }
// else
// kDebug() << "FAILED TO GET ICON!!!";
#endif
itsDlg->removeEventFilter(this);
}
return false;
}
KDialogDFileDialog::KDialogDFileDialog(QString &an, Operation op, const QString &startDir,
const QString &filter, bool confirmOw)
: KFileDialog(KUrl(startDir.isEmpty() || "~"==startDir ? QDir::homePath() : startDir),
filter, NULL),
itsConfirmOw(confirmOw),
itsDone(false),
itsAppName(an)
{
setModal(false);
setSelection(startDir);
kDebug() << "startDir:" << startDir;
switch(op)
{
case OP_FILE_OPEN:
setOperationMode(KFileDialog::Opening);
setMode(KFile::File|KFile::ExistingOnly);
break;
case OP_FILE_OPEN_MULTIPLE:
setOperationMode(KFileDialog::Opening);
setMode(KFile::Files|KFile::ExistingOnly);
break;
case OP_FILE_SAVE:
setOperationMode(KFileDialog::Saving);
setMode(KFile::File);
break;
default:
break;
}
if(KDialogD::config())
{
KConfigGroup cfg(KDialogD::config(), groupName(itsAppName));
//TODO !!! readConfig(KDialogD::config(), grp);
resize(cfg.readEntry(CFG_KEY_DIALOG_SIZE, QSize(600, 400)));
}
//TODO !!! ops->clearHistory();
}
void KDialogDFileDialog::accept()
{
fileWidget()->accept();
kDebug() << "KDialogDFileDialog::slotOk" << selectedUrls().count() << ' ' << mode() << ' ' << selectedUrl().prettyUrl();
KUrl::List urls(selectedUrls());
QStringList items;
bool good=true;
if(urls.count())
{
urls2Local(urls, items, this);
if(urls.count()!=items.count())
{
KMessageBox::sorry(this, i18n("You can only select local files."),
i18n("Remote Files Not Accepted"));
good=false;
}
else if(itsConfirmOw && KFileDialog::Saving==operationMode())
good=!KIO::NetAccess::exists(urls.first(), KIO::NetAccess::DestinationSide, this) ||
KMessageBox::Continue==KMessageBox::warningContinueCancel(this,
i18n("File %1 exits.\nDo you want to replace it?")
.arg(urls.first().prettyUrl()),
i18n("File Exists"),
KGuiItem(i18n("Replace"), "filesaveas"), KStandardGuiItem::cancel(), QString(),
KMessageBox::Notify|KMessageBox::PlainCaption);
if(good)
{
QString filter(currentFilter());
if(!filter.isEmpty())
items.append(filter);
emit ok(items);
hide();
//KFileDialog::accept();
}
else
setResult(QDialog::Rejected);
}
}
KDialogDFileDialog::~KDialogDFileDialog()
{
kDebug() << "~KDialogDFileDialog";
if(KDialogD::config())
{
KConfigGroup cfg(KDialogD::config(), groupName(itsAppName));
//TODO !!! writeConfig(KDialogD::config(), grp);
cfg.writeEntry(CFG_KEY_DIALOG_SIZE, size());
}
}
KDialogDDirSelectDialog::KDialogDDirSelectDialog(QString &an, const QString &startDir, bool localOnly,
QWidget *parent, bool modal)
: KDirSelectDialog(KUrl(startDir.isEmpty() || "~"==startDir
? QDir::homePath() : startDir),
localOnly, parent),
itsAppName(an)
{
kDebug() << "startDir:" << startDir;
setModal(false);
if(KDialogD::config())
{
KConfigGroup cfg(KDialogD::config(), groupName(itsAppName, false));
//TODO !!! readConfig(KDialogD::config(), grp);
resize(cfg.readEntry(CFG_KEY_DIALOG_SIZE, QSize(600, 400)));
}
}
KDialogDDirSelectDialog::~KDialogDDirSelectDialog()
{
kDebug() << "~KDialogDDirSelectDialog";
if(KDialogD::config())
{
KConfigGroup cfg(KDialogD::config(), groupName(itsAppName, false));
//TODO !!! writeConfig(KDialogD::config(), grp);
cfg.writeEntry(CFG_KEY_DIALOG_SIZE, size());
}
}
void KDialogDDirSelectDialog::slotOk()
{
kDebug() << "KDialogDDirSelectDialog::slotOk";
KUrl::List urls;
QStringList items;
urls.append(url());
urls2Local(urls, items, this);
if(urls.count()!=items.count())
KMessageBox::sorry(this, i18n("You can only select local folders."),
i18n("Remote Folders Not Accepted"));
else
{
emit ok(items);
hide();
}
}
#ifdef KDIALOGD_APP
static KAboutData aboutData("kdialogd4", "kdialogd4", ki18n("KDialog Daemon"), VERSION,
ki18n("Use KDE dialogs from non-KDE apps."),
KAboutData::License_GPL,
ki18n("(c) Craig Drummond, 2006-2007"));
int main(int argc, char **argv)
{
KCmdLineArgs::init(argc, argv, &aboutData);
KUniqueApplication *app=new KUniqueApplication;
KDialogD kdialogd;
QApplication::setQuitOnLastWindowClosed(false);
int rv=app->exec();
delete app;
unlink(getSockName());
releaseLock();
return rv;
}
#else
extern "C"
{
KDE_EXPORT KDEDModule *create_kdialogd()
{
return new KDialogDKDED();
}
};
KDialogDKDED::KDialogDKDED()
: KDEDModule()
{
new KDialogD(this);
}
#endif
#include "kdialogd.moc"