diff options
Diffstat (limited to 'kdialogd3/kdialogd.cpp')
-rw-r--r-- | kdialogd3/kdialogd.cpp | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/kdialogd3/kdialogd.cpp b/kdialogd3/kdialogd.cpp new file mode 100644 index 0000000..8aee15e --- /dev/null +++ b/kdialogd3/kdialogd.cpp @@ -0,0 +1,721 @@ +//#define USE_KWIN + +#include "kdialogd.h" +#include <iostream> +#include <kdiroperator.h> +#include <kuniqueapplication.h> +#include <qsocketnotifier.h> +#include <kio/netaccess.h> +#include <kmessagebox.h> +#include <klocale.h> +#include <kconfig.h> +#include <kurlcombobox.h> +#ifdef USE_KWIN +#include <kwin.h> +#else +#include <X11/Xlib.h> +#endif +#include <sys/un.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <ksockaddr.h> +#include <kdebug.h> +#include <kdeversion.h> +#include <qtimer.h> +#ifdef KDIALOGD_APP +#include <kcmdlineargs.h> +#include <kaboutdata.h> +#endif +#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 +static int createSocket() +{ + int sockitsFd; + ksocklen_t addrlen; + struct stat s; + const char *sock=getSockName(); + int stat_err=lstat(sock, &s); + + if(!stat_err && S_ISLNK(s.st_mode)) + { + kdWarning() << "Someone is running a symlink attack on you" << endl; + if(unlink(sock)) + { + kdWarning() << "Could not delete symlink" << endl; + return -1; + } + } + + if (!access(sock, R_OK|W_OK)) + { + kdWarning() << "stale socket exists" << endl; + if (unlink(sock)) + { + kdWarning() << "Could not delete stale socket" << endl; + return -1; + } + } + + sockitsFd = socket(PF_UNIX, SOCK_STREAM, 0); + if (sockitsFd < 0) + { + kdError() << "socket(): " << errno << endl; + 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(sockitsFd, (struct sockaddr *)&addr, addrlen) < 0) + { + kdError() << "bind(): " << errno << endl; + return -1; + } + + struct linger lin; + lin.l_onoff = lin.l_linger = 0; + if (setsockopt(sockitsFd, SOL_SOCKET, SO_LINGER, (char *) &lin, sizeof(linger)) < 0) + { + kdError() << "setsockopt(SO_LINGER): " << errno << endl; + return -1; + } + + int opt = 1; + if (setsockopt(sockitsFd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) + { + kdError() << "setsockopt(SO_REUSEADDR): " << errno << endl; + return -1; + } + opt = 1; + if (setsockopt(sockitsFd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, sizeof(opt)) < 0) + { + kdError() << "setsockopt(SO_KEEPALIVE): " << errno << endl; + return -1; + } + + if(::listen(sockitsFd, 1)<0) + { + kdError() << "listen(1): " << errno << endl; + return -1; + } + + //chmod(sock, 0600); + return sockitsFd; +} + +static void urls2Local(KURL::List &urls, QStringList &items, QWidget *parent) +{ + KURL::List::Iterator it(urls.begin()), + end(urls.end()); + + for(; it!=end; ++it) + if((*it).isLocalFile()) + items.append((*it).path()); + else + { +#if KDE_IS_VERSION(3, 5, 0) + KURL url(KIO::NetAccess::mostLocalURL(*it, parent)); + + if(url.isLocalFile()) + items.append(url.path()); + else + break; +#else + break; +#endif + } +} + +KDialogD::KDialogD(QObject *parent) + : QObject(parent), +#ifdef KDIALOGD_APP + itsTimer(NULL), + itsTimeoutVal(DEFAULT_TIMEOUT), +#endif + itsFd(::createSocket()), + itsNumConnections(0) +{ + if(itsFd<0) + { + kdError() << "KDialogD could not create socket" << endl; +#ifdef KDIALOGD_APP + kapp->exit(); +#endif + } + else + { + std::ofstream f(getPidFileName()); + + if(f) + { + f << getpid(); + f.close(); + } + if(!theirConfig) + theirConfig=new KConfig("kdialogdrc", false, false); + + connect(new QSocketNotifier(itsFd, QSocketNotifier::Read, this), + SIGNAL(activated(int)), this, SLOT(newConnection())); + +#ifdef KDIALOGD_APP + if(theirConfig->hasGroup(CFG_TIMEOUT_GROUP)) + { + theirConfig->setGroup(CFG_TIMEOUT_GROUP); + itsTimeoutVal=theirConfig->readNumEntry(CFG_TIMEOUT_KEY, DEFAULT_TIMEOUT); + if(itsTimeoutVal<0) + itsTimeoutVal=DEFAULT_TIMEOUT; + theirConfig->setGroup(QString::null); + } + + kdDebug() << "Timeout:" << itsTimeoutVal << endl; + if(itsTimeoutVal) + connect(itsTimer=new QTimer(this), SIGNAL(timeout()), this, SLOT(timeout())); +#endif + } +} + +KDialogD::~KDialogD() +{ + if(-1!=itsFd) + close(itsFd); + if(theirConfig) + delete theirConfig; + theirConfig=NULL; +} + +void KDialogD::newConnection() +{ + kdDebug() << "New connection" << endl; + + 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; + QCString 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) +{ + kdDebug() << "Delete client" << endl; + delete client; + +#ifdef KDIALOGD_APP + if(0==--itsNumConnections) + if(itsTimeoutVal) + itsTimer->start(itsTimeoutVal*1000, true); // Only single shot... + else + timeout(); +#endif +} + +void KDialogD::timeout() +{ +#ifdef KDIALOGD_APP + if(0==itsNumConnections) + if(grabLock(0)>0) // 0=> no wait... + { + kdDebug() << "Timeout occured, and no connections, so exit" << endl; + kapp->exit(); + } + else //...unlock lock file... + { + kdDebug() << "Timeout occured, but unable to grab lock file - app must be connecting" << endl; + releaseLock(); + } +#endif +} + +KDialogDClient::KDialogDClient(int sock, const QString &an, QObject *parent) + : QObject(parent), + itsFd(sock), + itsDlg(NULL), + itsAccepted(false), + itsAppName(an) +{ + kdDebug() << "new client..." << itsAppName << " (" << itsFd << ")" << endl; + 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() +{ + kdDebug() << "Deleted client" << endl; + if(-1!=itsFd) + ::close(itsFd); + if(KDialogD::config()) + KDialogD::config()->sync(); +} + +void KDialogDClient::close() +{ + kdDebug() << "close " << itsFd << endl; + + ::close(itsFd); + itsFd=-1; + if(itsDlg) + { + itsDlg->close(); + itsDlg->delayedDestruct(); + itsDlg=NULL; + } + emit error(this); +} + +void KDialogDClient::read() +{ + kdDebug() << "read" << endl; + + if(-1==itsFd) + return; + + char request; + QString caption; + unsigned int xid=0; + + if(!itsDlg && readData(&request, 1) && request>=(char)OP_FILE_OPEN && request<=(char)OP_FOLDER && + readData((char *)&xid, 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, + "folderdialog", false), xid); + 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), xid); + return; + } + } + } + + kdDebug() << "Comms error, closing connection..." << itsFd << endl; + // 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. + kdDebug() << "finished" << endl; + if(itsDlg && !(itsAccepted || QDialog::Accepted==itsDlg->result())) + cancel(); +} + +void KDialogDClient::ok(const QStringList &items) +{ + kdDebug() << "ok" << endl; + + int num=items.count(); + QStringList::ConstIterator it(items.begin()), + end(items.end()); + bool error=!writeData((char *)&num, 4); + + for(; !error && it!=end; ++it) + error=!writeString(*it); + + if(error) + close(); + else + itsAccepted=true; + if(itsDlg) + itsDlg->delayedDestruct(); + itsDlg=NULL; +} + +void KDialogDClient::cancel() +{ + if(itsDlg) + { + kdDebug() << "cancel" << endl; + + int rv=0; + + if(!writeData((char *)&rv, 4)) + close(); + if(itsDlg) + itsDlg->delayedDestruct(); + itsDlg=NULL; + } +} + +bool KDialogDClient::readData(QCString &buffer, int size) +{ + buffer.resize(size); + return ::readBlock(itsFd, buffer.data(), size); +} + +bool KDialogDClient::readString(QString &str) +{ + int size; + + if(!readData((char *)&size, 4)) + return false; + + QCString buffer; + buffer.resize(size); + + if(!readData(buffer.data(), size)) + return false; + + str=QString::fromUtf8(buffer.data()); + return true; +} + +bool KDialogDClient::writeString(const QString &str) +{ + QCString utf8(str.utf8()); + + int size=utf8.length()+1; + + return writeData((char *)&size, 4) && writeData(utf8.data(), size); +} + +void KDialogDClient::initDialog(const QString &caption, KDialogBase *d, unsigned int xid) +{ + itsAccepted=false; + itsDlg=d; + + if(!caption.isEmpty()) + itsDlg->setPlainCaption(caption); + + if(xid) + { +#ifdef USE_KWIN + KWin::setMainWindow(itsDlg, xid); + KWin::setState(itsDlg->winId(), NET::Modal); + + KWin::WindowInfo wi(KWin::windowInfo(xid, 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); +#else + XWindowAttributes attr; + int rx, ry; + Window junkwin; + + XSetTransientForHint(qt_xdisplay(), itsDlg->winId(), xid); + if(XGetWindowAttributes(qt_xdisplay(), xid, &attr)) + { + XTranslateCoordinates(qt_xdisplay(), xid, 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 + } + + connect(itsDlg, SIGNAL(ok(const QStringList &)), this, SLOT(ok(const QStringList &))); + connect(itsDlg, SIGNAL(finished()), this, SLOT(finished())); + itsDlg->show(); +} + +KDialogDFileDialog::KDialogDFileDialog(QString &an, Operation op, const QString &startDir, + const QString &filter, bool confirmOw) + : KFileDialog(startDir.isEmpty() || "~"==startDir ? QDir::homeDirPath() : startDir, + filter, NULL, NULL, false), + itsConfirmOw(confirmOw), + itsAppName(an) +{ + kdDebug() << "startDir:" << startDir << endl; + + switch(op) + { + case OP_FILE_OPEN: + setOperationMode(KFileDialog::Opening); + setMode((KFile::Mode)(KFile::File | KFile::ExistingOnly)); + break; + case OP_FILE_OPEN_MULTIPLE: + setOperationMode(KFileDialog::Opening); + setMode((KFile::Mode)(KFile::Files | KFile::ExistingOnly)); + break; + case OP_FILE_SAVE: + setOperationMode(KFileDialog::Saving); + setMode(KFile::File); + break; + default: + break; + } + + if(KDialogD::config()) + { + QString oldGrp(KDialogD::config()->group()), + grp(groupName(itsAppName)); + QSize defaultSize(600, 400); + + readConfig(KDialogD::config(), grp); + KDialogD::config()->setGroup(grp); + resize(KDialogD::config()->readSizeEntry(CFG_KEY_DIALOG_SIZE, &defaultSize)); + KDialogD::config()->setGroup(oldGrp); + } + + ops->clearHistory(); +} + +void KDialogDFileDialog::accept() +{ + kdDebug() << "KDialogDFileDialog::accept" << endl; +} + +void KDialogDFileDialog::slotOk() +{ + setResult(QDialog::Accepted); + KFileDialog::slotOk(); + + kdDebug() << "KDialogDFileDialog::slotOk" << endl; + KURL::List urls; + QStringList items; + bool good=true; + + if(mode()&KFile::Files) + urls=selectedURLs(); + else if(!locationEdit->currentText().isEmpty()) + urls.append(selectedURL()); + + 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(), false, 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"), QString::null, + 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() +{ + kdDebug() << "~KDialogDFileDialog" << endl; + + if(KDialogD::config()) + { + QString oldGrp(KDialogD::config()->group()), + grp(groupName(itsAppName)); + + writeConfig(KDialogD::config(), grp); + KDialogD::config()->setGroup(grp); + KDialogD::config()->writeEntry(CFG_KEY_DIALOG_SIZE, size()); + KDialogD::config()->setGroup(oldGrp); + } +} + +KDialogDDirSelectDialog::KDialogDDirSelectDialog(QString &an, const QString &startDir, bool localOnly, + QWidget *parent, const char *name, bool modal) + : KDirSelectDialog(startDir.isEmpty() || "~"==startDir + ? QDir::homeDirPath() : startDir, + localOnly, parent, name, modal), + itsAppName(an) +{ + kdDebug() << "startDir:" << startDir << endl; + + if(KDialogD::config()) + { + QString oldGrp(KDialogD::config()->group()), + grp(groupName(itsAppName, false)); + QSize defaultSize(600, 400); + + //readConfig(KDialogD::config(), grp); + KDialogD::config()->setGroup(grp); + resize(KDialogD::config()->readSizeEntry(CFG_KEY_DIALOG_SIZE, &defaultSize)); + KDialogD::config()->setGroup(oldGrp); + } +} + +KDialogDDirSelectDialog::~KDialogDDirSelectDialog() +{ + kdDebug() << "~KDialogDDirSelectDialog" << endl; + + if(KDialogD::config()) + { + QString oldGrp(KDialogD::config()->group()), + grp(groupName(itsAppName, false)); + + //writeConfig(KDialogD::config(), grp); + KDialogD::config()->setGroup(grp); + KDialogD::config()->writeEntry(CFG_KEY_DIALOG_SIZE, size()); + KDialogD::config()->setGroup(oldGrp); + } +} + +void KDialogDDirSelectDialog::slotOk() +{ + kdDebug() << "KDialogDDirSelectDialog::slotOk" << endl; + + 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("kdialogd3", I18N_NOOP("KDialog Daemon"), VERSION, + I18N_NOOP("Use KDE dialogs from non-KDE apps."), + KAboutData::License_GPL, + I18N_NOOP("(c) Craig Drummond, 2006-2007")); + +int main(int argc, char **argv) +{ + KCmdLineArgs::init(argc, argv, &aboutData); + + KUniqueApplication *app=new KUniqueApplication; + KDialogD kdialogd; + int rv=app->exec(); + + delete app; + + unlink(getSockName()); + releaseLock(); + return rv; +} +#else +extern "C" +{ + KDE_EXPORT KDEDModule *create_kdialogd(const QCString &obj) + { + return new KDialogDKDED(obj); + } +}; + +KDialogDKDED::KDialogDKDED(const QCString &obj) + : KDEDModule(obj) +{ + new KDialogD(this); +} +#endif + +#include "kdialogd.moc" + |