//#define USE_KWIN #include "kdialogd.h" #include #include #include #include #include #include #include #include #include #ifdef USE_KWIN #include #else #include #endif #include #include #include #include #include #include #include #include #ifdef KDIALOGD_APP #include #include #endif #include 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 TQString groupName(const TQString &app, bool fileDialog=true) { return TQString(fileDialog ? "KFileDialog " : "KDirSelectDialog ")+app; } // from tdebase/tdesu 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, TQStringList &items, TQWidget *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(TQObject *parent) : TQObject(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 TQSocketNotifier(itsFd, TQSocketNotifier::Read, this), TQT_SIGNAL(activated(int)), this, TQT_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(TQString()); } kdDebug() << "Timeout:" << itsTimeoutVal << endl; if(itsTimeoutVal) connect(itsTimer=new TQTimer(this), TQT_SIGNAL(timeout()), this, TQT_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; TQCString 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), TQT_SIGNAL(error(KDialogDClient *)), this, TQT_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 TQString &an, TQObject *parent) : TQObject(parent), itsFd(sock), itsDlg(NULL), itsAccepted(false), itsAppName(an) { kdDebug() << "new client..." << itsAppName << " (" << itsFd << ")" << endl; connect(new TQSocketNotifier(itsFd, TQSocketNotifier::Read, this), TQT_SIGNAL(activated(int)), this, TQT_SLOT(read())); connect(new TQSocketNotifier(itsFd, TQSocketNotifier::Exception, this), TQT_SIGNAL(activated(int)), this, TQT_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; TQString 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) { TQString intialFolder; if(readString(intialFolder)) { initDialog(caption, new KDialogDDirSelectDialog(itsAppName, intialFolder, true, NULL, "folderdialog", false), xid); return; } } else { TQString 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 TQDialog 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 || TQDialog::Accepted==itsDlg->result())) cancel(); } void KDialogDClient::ok(const TQStringList &items) { kdDebug() << "ok" << endl; int num=items.count(); TQStringList::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(TQCString &buffer, int size) { buffer.resize(size); return ::readBlock(itsFd, buffer.data(), size); } bool KDialogDClient::readString(TQString &str) { int size; if(!readData((char *)&size, 4)) return false; TQCString buffer; buffer.resize(size); if(!readData(buffer.data(), size)) return false; str=TQString::fromUtf8(buffer.data()); return true; } bool KDialogDClient::writeString(const TQString &str) { TQCString utf8(str.utf8()); int size=utf8.length()+1; return writeData((char *)&size, 4) && writeData(utf8.data(), size); } void KDialogDClient::initDialog(const TQString &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)); TQRect 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 juntwin; XSetTransientForHint(tqt_xdisplay(), itsDlg->winId(), xid); if(XGetWindowAttributes(tqt_xdisplay(), xid, &attr)) { XTranslateCoordinates(tqt_xdisplay(), xid, attr.root, -attr.border_width, -16, &rx, &ry, &juntwin); 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, TQT_SIGNAL(ok(const TQStringList &)), this, TQT_SLOT(ok(const TQStringList &))); connect(itsDlg, TQT_SIGNAL(finished()), this, TQT_SLOT(finished())); itsDlg->show(); } KDialogDFileDialog::KDialogDFileDialog(TQString &an, Operation op, const TQString &startDir, const TQString &filter, bool confirmOw) : KFileDialog(startDir.isEmpty() || "~"==startDir ? TQDir::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()) { TQString oldGrp(KDialogD::config()->group()), grp(groupName(itsAppName)); TQSize 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(TQDialog::Accepted); KFileDialog::slotOk(); kdDebug() << "KDialogDFileDialog::slotOk" << endl; KURL::List urls; TQStringList 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 exists.\nDo you want to replace it?") .arg(urls.first().prettyURL()), i18n("File Exists"), KGuiItem(i18n("Replace"), "filesaveas"), TQString(), KMessageBox::Notify|KMessageBox::PlainCaption); if(good) { TQString filter(currentFilter()); if(!filter.isEmpty()) items.append(filter); emit ok(items); hide(); KFileDialog::accept(); } else setResult(TQDialog::Rejected); } } KDialogDFileDialog::~KDialogDFileDialog() { kdDebug() << "~KDialogDFileDialog" << endl; if(KDialogD::config()) { TQString 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(TQString &an, const TQString &startDir, bool localOnly, TQWidget *parent, const char *name, bool modal) : KDirSelectDialog(startDir.isEmpty() || "~"==startDir ? TQDir::homeDirPath() : startDir, localOnly, parent, name, modal), itsAppName(an) { kdDebug() << "startDir:" << startDir << endl; if(KDialogD::config()) { TQString oldGrp(KDialogD::config()->group()), grp(groupName(itsAppName, false)); TQSize 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()) { TQString 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; TQStringList 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 TQCString &obj) { return new KDialogDKDED(obj); } }; KDialogDKDED::KDialogDKDED(const TQCString &obj) : KDEDModule(obj) { new KDialogD(this); } #endif #include "kdialogd.moc"