summaryrefslogtreecommitdiffstats
path: root/kdialogd4/kdialogd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdialogd4/kdialogd.cpp')
-rw-r--r--kdialogd4/kdialogd.cpp759
1 files changed, 759 insertions, 0 deletions
diff --git a/kdialogd4/kdialogd.cpp b/kdialogd4/kdialogd.cpp
new file mode 100644
index 0000000..ddd1142
--- /dev/null
+++ b/kdialogd4/kdialogd.cpp
@@ -0,0 +1,759 @@
+#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"
+