diff options
Diffstat (limited to 'kdialogd4/kdialogd.cpp')
-rw-r--r-- | kdialogd4/kdialogd.cpp | 759 |
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" + |