dcopclient.cpp
/*****************************************************************
Copyright (c) 1999 Preston Brown <pbrown@kde.org>
Copyright (c) 1999 Matthias Ettrich <ettrich@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************/
// qt <-> dcop integration
#include <qobjectlist.h>
#include <qmetaobject.h>
#include <qvariant.h>
#include <qtimer.h>
#include <qintdict.h>
#include <qeventloop.h>
// end of qt <-> dcop integration
#include "config.h"
#include <config.h>
#include <dcopref.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#ifndef QT_CLEAN_NAMESPACE
#define QT_CLEAN_NAMESPACE
#endif
#include <qguardedptr.h>
#include <qtextstream.h>
#include <qfile.h>
#include <qdir.h>
#include <qapplication.h>
#include <qsocketnotifier.h>
#include <qregexp.h>
#include <private/qucomextra_p.h>
#include <dcopglobal.h>
#include <dcopclient.h>
#include <dcopobject.h>
#if defined Q_WS_X11 && ! defined K_WS_QTONLY
#include <X11/Xmd.h>
#endif
extern "C" {
#include <KDE-ICE/ICElib.h>
#include <KDE-ICE/ICEutil.h>
#include <KDE-ICE/ICEmsg.h>
#include <KDE-ICE/ICEproto.h>
}
// #define DCOPCLIENT_DEBUG 1
extern QMap<QCString, DCOPObject *> * kde_dcopObjMap; // defined in dcopobject.cpp
/*********************************************
* Keep track of local clients
*********************************************/
typedef QAsciiDict<DCOPClient> client_map_t;
static client_map_t *DCOPClient_CliMap = 0;
static
client_map_t *cliMap()
{
if (!DCOPClient_CliMap)
DCOPClient_CliMap = new client_map_t;
return DCOPClient_CliMap;
}
DCOPClient *DCOPClient::findLocalClient( const QCString &_appId )
{
return cliMap()->find(_appId.data());
}
static
void registerLocalClient( const QCString &_appId, DCOPClient *client )
{
cliMap()->replace(_appId.data(), client);
}
static
void unregisterLocalClient( const QCString &_appId )
{
client_map_t *map = cliMap();
map->remove(_appId.data());
}
template class QPtrList<DCOPObjectProxy>;
template class QPtrList<DCOPClientTransaction>;
template class QPtrList<_IceConn>;
struct DCOPClientMessage
{
int opcode;
CARD32 key;
QByteArray data;
};
class DCOPClient::ReplyStruct
{
public:
enum ReplyStatus { Pending, Ok, Failed };
ReplyStruct() {
status = Pending;
replyType = 0;
replyData = 0;
replyId = -1;
transactionId = -1;
replyObject = 0;
}
ReplyStatus status;
QCString* replyType;
QByteArray* replyData;
int replyId;
Q_INT32 transactionId;
QCString calledApp;
QGuardedPtr<QObject> replyObject;
QCString replySlot;
};
class DCOPClientPrivate
{
public:
DCOPClient *parent;
QCString appId;
IceConn iceConn;
int majorOpcode; // major opcode negotiated w/server and used to tag all comms.
int majorVersion, minorVersion; // protocol versions negotiated w/server
static const char* serverAddr; // location of server in ICE-friendly format.
QSocketNotifier *notifier;
bool non_blocking_call_lock;
bool registered;
bool foreign_server;
bool accept_calls;
bool accept_calls_override; // If true, user has specified policy.
bool qt_bridge_enabled;
QCString senderId;
QCString objId;
QCString function;
QCString defaultObject;
QPtrList<DCOPClientTransaction> *transactionList;
bool transaction;
Q_INT32 transactionId;
int opcode;
// Special key values:
// 0 : Not specified
// 1 : DCOPSend
// 2 : Priority
// >= 42: Normal
CARD32 key;
CARD32 currentKey;
CARD32 currentKeySaved;
QTimer postMessageTimer;
QPtrList<DCOPClientMessage> messages;
QPtrList<DCOPClient::ReplyStruct> pendingReplies;
QPtrList<DCOPClient::ReplyStruct> asyncReplyQueue;
struct LocalTransactionResult
{
QCString replyType;
QByteArray replyData;
};
QIntDict<LocalTransactionResult> localTransActionList;
QTimer eventLoopTimer;
};
class DCOPClientTransaction
{
public:
Q_INT32 id;
CARD32 key;
QCString senderId;
};
QCString DCOPClient::iceauthPath()
{
#ifdef Q_OS_WIN32
char szPath[512];
char * pszFilePart;
int ret;
ret = SearchPathA(NULL,"iceauth.exe",NULL,sizeof(szPath)/sizeof(szPath[0]),szPath,&pszFilePart);
if(ret != 0)
return QCString(szPath);
#else
QCString path = ::getenv("PATH");
if (path.isEmpty())
path = "/bin:/usr/bin";
path += ":/usr/bin/X11:/usr/X11/bin:/usr/X11R6/bin";
QCString fPath = strtok(path.data(), ":\b");
while (!fPath.isNull())
{
fPath += "/iceauth";
if (access(fPath.data(), X_OK) == 0)
{
return fPath;
}
fPath = strtok(NULL, ":\b");
}
#endif
return 0;
}
static QCString dcopServerFile(const QCString &hostname, bool old)
{
QCString fName = ::getenv("DCOPAUTHORITY");
if (!old && !fName.isEmpty())
return fName;
fName = QFile::encodeName( QDir::homeDirPath() );
// fName = ::getenv("HOME");
if (fName.isEmpty())
{
fprintf(stderr, "Aborting. $HOME is not set.\n");
exit(1);
}
#ifdef Q_WS_X11
QCString disp = getenv("DISPLAY");
#elif defined(Q_WS_QWS)
QCString disp = getenv("QWS_DISPLAY");
#else
QCString disp;
#endif
if (disp.isEmpty())
disp = "NODISPLAY";
int i;
if((i = disp.findRev('.')) > disp.findRev(KPATH_SEPARATOR) && i >= 0)
disp.truncate(i);
if (!old)
{
while( (i = disp.find(KPATH_SEPARATOR)) >= 0)
disp[i] = '_';
}
fName += "/.DCOPserver_";
if (hostname.isEmpty())
{
char hostName[256];
hostName[0] = '\0';
if (gethostname(hostName, sizeof(hostName)))
{
fName += "localhost";
}
else
{
hostName[sizeof(hostName)-1] = '\0';
fName += hostName;
}
}
else
{
fName += hostname;
}
fName += "_"+disp;
return fName;
}
// static
QCString DCOPClient::dcopServerFile(const QCString &hostname)
{
return ::dcopServerFile(hostname, false);
}
// static
QCString DCOPClient::dcopServerFileOld(const QCString &hostname)
{
return ::dcopServerFile(hostname, true);
}
const char* DCOPClientPrivate::serverAddr = 0;
static void DCOPProcessInternal( DCOPClientPrivate *d, int opcode, CARD32 key, const QByteArray& dataReceived, bool canPost );
void DCOPClient::handleAsyncReply(ReplyStruct *replyStruct)
{
if (replyStruct->replyObject)
{
QObject::connect(this, SIGNAL(callBack(int, const QCString&, const QByteArray &)),
replyStruct->replyObject, replyStruct->replySlot);
emit callBack(replyStruct->replyId, *(replyStruct->replyType), *(replyStruct->replyData));
QObject::disconnect(this, SIGNAL(callBack(int, const QCString&, const QByteArray &)),
replyStruct->replyObject, replyStruct->replySlot);
}
delete replyStruct;
}
static void DCOPProcessMessage(IceConn iceConn, IcePointer clientObject,
int opcode, unsigned long length, Bool /*swap*/,
IceReplyWaitInfo *replyWait,
Bool *replyWaitRet)
{
DCOPMsg *pMsg = 0;
DCOPClientPrivate *d = static_cast<DCOPClientPrivate *>(clientObject);
DCOPClient::ReplyStruct *replyStruct = replyWait ? static_cast<DCOPClient::ReplyStruct*>(replyWait->reply) : 0;
IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
CARD32 key = pMsg->key;
if ( d->key == 0 )
d->key = key; // received a key from the server
QByteArray dataReceived( length );
IceReadData(iceConn, length, dataReceived.data() );
d->opcode = opcode;
switch (opcode ) {
case DCOPReplyFailed:
if ( replyStruct ) {
replyStruct->status = DCOPClient::ReplyStruct::Failed;
replyStruct->transactionId = 0;
*replyWaitRet = True;
return;
} else {
qWarning("Very strange! got a DCOPReplyFailed opcode, but we were not waiting for a reply!");
return;
}
case DCOPReply:
if ( replyStruct ) {
QByteArray* b = replyStruct->replyData;
QCString* t = replyStruct->replyType;
replyStruct->status = DCOPClient::ReplyStruct::Ok;
replyStruct->transactionId = 0;
QCString calledApp, app;
QDataStream ds( dataReceived, IO_ReadOnly );
ds >> calledApp >> app >> *t >> *b;
*replyWaitRet = True;
return;
} else {
qWarning("Very strange! got a DCOPReply opcode, but we were not waiting for a reply!");
return;
}
case DCOPReplyWait:
if ( replyStruct ) {
QCString calledApp, app;
Q_INT32 id;
QDataStream ds( dataReceived, IO_ReadOnly );
ds >> calledApp >> app >> id;
replyStruct->transactionId = id;
replyStruct->calledApp = calledApp;
d->pendingReplies.append(replyStruct);
*replyWaitRet = True;
return;
} else {
qWarning("Very strange! got a DCOPReplyWait opcode, but we were not waiting for a reply!");
return;
}
case DCOPReplyDelayed:
{
QDataStream ds( dataReceived, IO_ReadOnly );
QCString calledApp, app;
Q_INT32 id;
ds >> calledApp >> app >> id;
if (replyStruct && (id == replyStruct->transactionId) && (calledApp == replyStruct->calledApp))
{
*replyWaitRet = True;
}
for(DCOPClient::ReplyStruct *rs = d->pendingReplies.first(); rs;
rs = d->pendingReplies.next())
{
if ((rs->transactionId == id) && (rs->calledApp == calledApp))
{
d->pendingReplies.remove();
QByteArray* b = rs->replyData;
QCString* t = rs->replyType;
ds >> *t >> *b;
rs->status = DCOPClient::ReplyStruct::Ok;
rs->transactionId = 0;
if (!rs->replySlot.isEmpty())
{
d->parent->handleAsyncReply(rs);
}
return;
}
}
}
qWarning("Very strange! got a DCOPReplyDelayed opcode, but we were not waiting for a reply!");
return;
case DCOPCall:
case DCOPFind:
case DCOPSend:
DCOPProcessInternal( d, opcode, key, dataReceived, true );
}
}
void DCOPClient::processPostedMessagesInternal()
{
if ( d->messages.isEmpty() )
return;
QPtrListIterator<DCOPClientMessage> it (d->messages );
DCOPClientMessage* msg ;
while ( ( msg = it.current() ) ) {
++it;
if ( d->currentKey && msg->key != d->currentKey )
continue;
d->messages.removeRef( msg );
d->opcode = msg->opcode;
DCOPProcessInternal( d, msg->opcode, msg->key, msg->data, false );
delete msg;
}
if ( !d->messages.isEmpty() )
d->postMessageTimer.start( 100, true );
}
void DCOPProcessInternal( DCOPClientPrivate *d, int opcode, CARD32 key, const QByteArray& dataReceived, bool canPost )
{
if (!d->accept_calls && (opcode == DCOPSend))
return;
IceConn iceConn = d->iceConn;
DCOPMsg *pMsg = 0;
DCOPClient *c = d->parent;
QDataStream ds( dataReceived, IO_ReadOnly );
QCString fromApp;
ds >> fromApp;
if (fromApp.isEmpty())
return; // Reserved for local calls
if (!d->accept_calls)
{
QByteArray reply;
QDataStream replyStream( reply, IO_WriteOnly );
// Call rejected.
replyStream << d->appId << fromApp;
IceGetHeader( iceConn, d->majorOpcode, DCOPReplyFailed,
sizeof(DCOPMsg), DCOPMsg, pMsg );
int datalen = reply.size();
pMsg->key = key;
pMsg->length += datalen;
IceSendData( iceConn, datalen, const_cast<char *>(reply.data()));
return;
}
QCString app, objId, fun;
QByteArray data;
ds >> app >> objId >> fun >> data;
d->senderId = fromApp;
d->objId = objId;
d->function = fun;
// qWarning("DCOP: %s got call: %s:%s:%s key = %d currentKey = %d", d->appId.data(), app.data(), objId.data(), fun.data(), key, d->currentKey);
if ( canPost && d->currentKey && key != d->currentKey ) {
DCOPClientMessage* msg = new DCOPClientMessage;
msg->opcode = opcode;
msg->key = key;
msg->data = dataReceived;
d->messages.append( msg );
d->postMessageTimer.start( 0, true );
return;
}
d->objId = objId;
d->function = fun;
QCString replyType;
QByteArray replyData;
bool b;
CARD32 oldCurrentKey = d->currentKey;
if ( opcode != DCOPSend ) // DCOPSend doesn't change the current key
d->currentKey = key;
if ( opcode == DCOPFind )
b = c->find(app, objId, fun, data, replyType, replyData );
else
b = c->receive( app, objId, fun, data, replyType, replyData );
// set notifier back to previous state
if ( opcode == DCOPSend )
return;
if ((d->currentKey == key) || (oldCurrentKey != 2))
d->currentKey = oldCurrentKey;
QByteArray reply;
QDataStream replyStream( reply, IO_WriteOnly );
Q_INT32 id = c->transactionId();
if (id) {
// Call delayed. Send back the transaction ID.
replyStream << d->appId << fromApp << id;
IceGetHeader( iceConn, d->majorOpcode, DCOPReplyWait,
sizeof(DCOPMsg), DCOPMsg, pMsg );
pMsg->key = key;
pMsg->length += reply.size();
IceSendData( iceConn, reply.size(), const_cast<char *>(reply.data()));
return;
}
if ( !b ) {
// Call failed. No data send back.
replyStream << d->appId << fromApp;
IceGetHeader( iceConn, d->majorOpcode, DCOPReplyFailed,
sizeof(DCOPMsg), DCOPMsg, pMsg );
int datalen = reply.size();
pMsg->key = key;
pMsg->length += datalen;
IceSendData( iceConn, datalen, const_cast<char *>(reply.data()));
return;
}
// Call successful. Send back replyType and replyData.
replyStream << d->appId << fromApp << replyType << replyData.size();
// we are calling, so we need to set up reply data
IceGetHeader( iceConn, d->majorOpcode, DCOPReply,
sizeof(DCOPMsg), DCOPMsg, pMsg );
int datalen = reply.size() + replyData.size();
pMsg->key = key;
pMsg->length += datalen;
// use IceSendData not IceWriteData to avoid a copy. Output buffer
// shouldn't need to be flushed.
IceSendData( iceConn, reply.size(), const_cast<char *>(reply.data()));
IceSendData( iceConn, replyData.size(), const_cast<char *>(replyData.data()));
}
static IcePoVersionRec DCOPClientVersions[] = {
{ DCOPVersionMajor, DCOPVersionMinor, DCOPProcessMessage }
};
static DCOPClient* dcop_main_client = 0;
DCOPClient* DCOPClient::mainClient()
{
return dcop_main_client;
}
void DCOPClient::setMainClient( DCOPClient* client )
{
dcop_main_client = client;
}
DCOPClient::DCOPClient()
{
d = new DCOPClientPrivate;
d->parent = this;
d->iceConn = 0L;
d->key = 0;
d->currentKey = 0;
d->majorOpcode = 0;
d->appId = 0;
d->notifier = 0L;
d->non_blocking_call_lock = false;
d->registered = false;
d->foreign_server = true;
d->accept_calls = true;
d->accept_calls_override = false;
d->qt_bridge_enabled = true;
d->transactionList = 0L;
d->transactionId = 0;
QObject::connect( &d->postMessageTimer, SIGNAL( timeout() ), this, SLOT( processPostedMessagesInternal() ) );
QObject::connect( &d->eventLoopTimer, SIGNAL( timeout() ), this, SLOT( eventLoopTimeout() ) );
if ( !mainClient() )
setMainClient( this );
}
DCOPClient::~DCOPClient()
{
#ifdef DCOPCLIENT_DEBUG
qWarning("d->messages.count() = %d", d->messages.count());
QPtrListIterator<DCOPClientMessage> it (d->messages );
DCOPClientMessage* msg ;
while ( ( msg = it.current() ) ) {
++it;
d->messages.removeRef( msg );
qWarning("DROPPING UNHANDLED DCOP MESSAGE:");
qWarning(" opcode = %d key = %d", msg->opcode, msg->key);
QDataStream ds( msg->data, IO_ReadOnly );
QCString fromApp, app, objId, fun;
ds >> fromApp >> app >> objId >> fun;
qWarning(" from = %s", fromApp.data());
qWarning(" to = %s / %s / %s", app.data(), objId.data(), fun.data());
delete msg;
}
#endif
if (d->iceConn)
if (IceConnectionStatus(d->iceConn) == IceConnectAccepted)
detach();
if (d->registered)
unregisterLocalClient( d->appId );
delete d->notifier;
delete d->transactionList;
d->messages.setAutoDelete(true);
delete d;
if ( mainClient() == this )
setMainClient( 0 );
}
void DCOPClient::setServerAddress(const QCString &addr)
{
QCString env = "DCOPSERVER=" + addr;
putenv(strdup(env.data()));
delete [] DCOPClientPrivate::serverAddr;
DCOPClientPrivate::serverAddr = qstrdup( addr.data() );
}
bool DCOPClient::attach()
{
if (!attachInternal( true ))
if (!attachInternal( true ))
return false; // Try two times!
return true;
}
void DCOPClient::bindToApp()
{
// check if we have a qApp instantiated. If we do,
// we can create a QSocketNotifier and use it for receiving data.
if (qApp) {
if ( d->notifier )
delete d->notifier;
d->notifier = new QSocketNotifier(socket(),
QSocketNotifier::Read, 0, 0);
QObject::connect(d->notifier, SIGNAL(activated(int)),
SLOT(processSocketData(int)));
}
}
void DCOPClient::suspend()
{
#ifdef Q_WS_WIN //TODO: remove (win32 ports sometimes do not create notifiers)
if (!d->notifier)
return;
#endif
assert(d->notifier); // Suspending makes no sense if we didn't had a qApp yet
d->notifier->setEnabled(false);
}
void DCOPClient::resume()
{
#ifdef Q_WS_WIN //TODO: remove
if (!d->notifier)
return;
#endif
assert(d->notifier); // Should never happen
d->notifier->setEnabled(true);
}
bool DCOPClient::isSuspended() const
{
#if defined(Q_WS_WIN) || defined(Q_WS_MAC) //TODO: REMOVE
if (!d->notifier)
return false;
#endif
return !d->notifier->isEnabled();
}
#ifdef SO_PEERCRED
// Check whether the remote end is owned by the same user.
static bool peerIsUs(int sockfd)
{
struct ucred cred;
socklen_t siz = sizeof(cred);
if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) != 0)
return false;
return (cred.uid == getuid());
}
#else
// Check whether the socket is owned by the same user.
static bool isServerSocketOwnedByUser(const char*server)
{
#ifdef Q_OS_WIN
if (strncmp(server, "tcp/", 4) != 0)
return false; // Not a local socket -> foreign.
else
return true;
#else
if (strncmp(server, "local/", 6) != 0)
return false; // Not a local socket -> foreign.
const char *path = strchr(server, KPATH_SEPARATOR);
if (!path)
return false;
path++;
struct stat stat_buf;
if (stat(path, &stat_buf) != 0)
return false;
return (stat_buf.st_uid == getuid());
#endif
}
#endif
bool DCOPClient::attachInternal( bool registerAsAnonymous )
{
char errBuf[1024];
if ( isAttached() )
detach();
if ((d->majorOpcode = IceRegisterForProtocolSetup(const_cast<char *>("DCOP"),
const_cast<char *>(DCOPVendorString),
const_cast<char *>(DCOPReleaseString),
1, DCOPClientVersions,
DCOPAuthCount,
const_cast<char **>(DCOPAuthNames),
DCOPClientAuthProcs, 0L)) < 0) {
emit attachFailed(QString::fromLatin1( "Communications could not be established." ));
return false;
}
bool bClearServerAddr = false;
// first, check if serverAddr was ever set.
if (!d->serverAddr) {
// here, we obtain the list of possible DCOP connections,
// and attach to them.
QCString dcopSrv;
dcopSrv = ::getenv("DCOPSERVER");
if (dcopSrv.isEmpty()) {
QCString fName = dcopServerFile();
QFile f(QFile::decodeName(fName));
if (!f.open(IO_ReadOnly)) {
emit attachFailed(QString::fromLatin1( "Could not read network connection list.\n" )+QFile::decodeName(fName));
return false;
}
int size = QMIN( 1024, f.size() ); // protection against a huge file
QCString contents( size+1 );
if ( f.readBlock( contents.data(), size ) != size )
{
qDebug("Error reading from %s, didn't read the expected %d bytes", fName.data(), size);
// Should we abort ?
}
contents[size] = '\0';
int pos = contents.find('\n');
if ( pos == -1 ) // Shouldn't happen
{
qDebug("Only one line in dcopserver file !: %s", contents.data());
dcopSrv = contents;
}
else
{
if(contents[pos - 1] == '\r') // check for windows end of line
pos--;
dcopSrv = contents.left( pos );
//#ifndef NDEBUG
// qDebug("dcopserver address: %s", dcopSrv.data());
//#endif
}
}
d->serverAddr = qstrdup( const_cast<char *>(dcopSrv.data()) );
bClearServerAddr = true;
}
if ((d->iceConn = IceOpenConnection(const_cast<char*>(d->serverAddr),
static_cast<IcePointer>(this), False, d->majorOpcode,
sizeof(errBuf), errBuf)) == 0L) {
qDebug("DCOPClient::attachInternal. Attach failed %s", errBuf);
d->iceConn = 0;
if (bClearServerAddr) {
delete [] d->serverAddr;
d->serverAddr = 0;
}
emit attachFailed(QString::fromLatin1( errBuf ));
return false;
}
fcntl(socket(), F_SETFL, FD_CLOEXEC);
IceSetShutdownNegotiation(d->iceConn, False);
int setupstat;
char* vendor = 0;
char* release = 0;
setupstat = IceProtocolSetup(d->iceConn, d->majorOpcode,
static_cast<IcePointer>(d),
False, /* must authenticate */
&(d->majorVersion), &(d->minorVersion),
&(vendor), &(release), 1024, errBuf);
if (vendor) free(vendor);
if (release) free(release);
if (setupstat == IceProtocolSetupFailure ||
setupstat == IceProtocolSetupIOError) {
IceCloseConnection(d->iceConn);
d->iceConn = 0;
if (bClearServerAddr) {
delete [] d->serverAddr;
d->serverAddr = 0;
}
emit attachFailed(QString::fromLatin1( errBuf ));
return false;
} else if (setupstat == IceProtocolAlreadyActive) {
if (bClearServerAddr) {
delete [] d->serverAddr;
d->serverAddr = 0;
}
/* should not happen because 3rd arg to IceOpenConnection was 0. */
emit attachFailed(QString::fromLatin1( "internal error in IceOpenConnection" ));
return false;
}
if (IceConnectionStatus(d->iceConn) != IceConnectAccepted) {
if (bClearServerAddr) {
delete [] d->serverAddr;
d->serverAddr = 0;
}
emit attachFailed(QString::fromLatin1( "DCOP server did not accept the connection." ));
return false;
}
#ifdef SO_PEERCRED
d->foreign_server = !peerIsUs(socket());
#else
d->foreign_server = !isServerSocketOwnedByUser(d->serverAddr);
#endif
if (!d->accept_calls_override)
d->accept_calls = !d->foreign_server;
bindToApp();
if ( registerAsAnonymous )
registerAs( "anonymous", true );
return true;
}
bool DCOPClient::detach()
{
int status;
if (d->iceConn) {
IceProtocolShutdown(d->iceConn, d->majorOpcode);
status = IceCloseConnection(d->iceConn);
if (status != IceClosedNow)
return false;
else
d->iceConn = 0L;
}
if (d->registered)
unregisterLocalClient(d->appId);
delete d->notifier;
d->notifier = 0L;
d->registered = false;
d->foreign_server = true;
return true;
}
bool DCOPClient::isAttached() const
{
if (!d->iceConn)
return false;
return (IceConnectionStatus(d->iceConn) == IceConnectAccepted);
}
bool DCOPClient::isAttachedToForeignServer() const
{
return isAttached() && d->foreign_server;
}
bool DCOPClient::acceptCalls() const
{
return isAttached() && d->accept_calls;
}
void DCOPClient::setAcceptCalls(bool b)
{
d->accept_calls = b;
d->accept_calls_override = true;
}
bool DCOPClient::qtBridgeEnabled()
{
return d->qt_bridge_enabled;
}
void DCOPClient::setQtBridgeEnabled(bool b)
{
d->qt_bridge_enabled = b;
}
QCString DCOPClient::registerAs( const QCString &appId, bool addPID )
{
QCString result;
QCString _appId = appId;
if (addPID) {
QCString pid;
pid.sprintf("-%d", getpid());
_appId = _appId + pid;
}
if( d->appId == _appId )
return d->appId;
#if 0 // no need to detach, dcopserver can handle renaming
// Detach before reregistering.
if ( isRegistered() ) {
detach();
}
#endif
if ( !isAttached() ) {
if (!attachInternal( false ))
if (!attachInternal( false ))
return result; // Try two times
}
// register the application identifier with the server
QCString replyType;
QByteArray data, replyData;
QDataStream arg( data, IO_WriteOnly );
arg << _appId;
if ( call( "DCOPServer", "", "registerAs(QCString)", data, replyType, replyData ) ) {
QDataStream reply( replyData, IO_ReadOnly );
reply >> result;
}
d->appId = result;
d->registered = !result.isNull();
if (d->registered)
registerLocalClient( d->appId, this );
return result;
}
bool DCOPClient::isRegistered() const
{
return d->registered;
}
QCString DCOPClient::appId() const
{
return d->appId;
}
int DCOPClient::socket() const
{
if (d->iceConn)
return IceConnectionNumber(d->iceConn);
return 0;
}
static inline bool isIdentChar( char x )
{ // Avoid bug in isalnum
return x == '_' || (x >= '0' && x <= '9') ||
(x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z');
}
QCString DCOPClient::normalizeFunctionSignature( const QCString& fun ) {
if ( fun.isEmpty() ) // nothing to do
return fun.copy();
QCString result( fun.size() );
char *from = fun.data();
char *to = result.data();
char *first = to;
char last = 0;
while ( true ) {
while ( *from && isspace(*from) )
from++;
if ( last && isIdentChar( last ) && isIdentChar( *from ) )
*to++ = 0x20;
while ( *from && !isspace(*from) ) {
last = *from++;
*to++ = last;
}
if ( !*from )
break;
}
if ( to > first && *(to-1) == 0x20 )
to--;
*to = '\0';
result.resize( (int)((long)to - (long)result.data()) + 1 );
return result;
}
QCString DCOPClient::senderId() const
{
return d->senderId;
}
bool DCOPClient::send(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QByteArray &data)
{
if (remApp.isEmpty())
return false;
DCOPClient *localClient = findLocalClient( remApp );
if ( localClient ) {
bool saveTransaction = d->transaction;
Q_INT32 saveTransactionId = d->transactionId;
QCString saveSenderId = d->senderId;
d->senderId = 0; // Local call
QCString replyType;
QByteArray replyData;
(void) localClient->receive( remApp, remObjId, remFun, data, replyType, replyData );
d->transaction = saveTransaction;
d->transactionId = saveTransactionId;
d->senderId = saveSenderId;
// send() returns true if the data could be send to the DCOPServer,
// regardles of receiving the data on the other application.
// So we assume the data is successfully send to the (virtual) server
// and return true in any case.
return true;
}
if ( !isAttached() )
return false;
DCOPMsg *pMsg;
QByteArray ba;
QDataStream ds(ba, IO_WriteOnly);
ds << d->appId << remApp << remObjId << normalizeFunctionSignature(remFun) << data.size();
IceGetHeader(d->iceConn, d->majorOpcode, DCOPSend,
sizeof(DCOPMsg), DCOPMsg, pMsg);
pMsg->key = 1; // DCOPSend always uses the magic key 1
int datalen = ba.size() + data.size();
pMsg->length += datalen;
IceSendData( d->iceConn, ba.size(), const_cast<char *>(ba.data()) );
IceSendData( d->iceConn, data.size(), const_cast<char *>(data.data()) );
//IceFlush(d->iceConn);
if (IceConnectionStatus(d->iceConn) == IceConnectAccepted)
return true;
return false;
}
bool DCOPClient::send(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QString &data)
{
QByteArray ba;
QDataStream ds(ba, IO_WriteOnly);
ds << data;
return send(remApp, remObjId, remFun, ba);
}
bool DCOPClient::findObject(const QCString &remApp, const QCString &remObj,
const QCString &remFun, const QByteArray &data,
QCString &foundApp, QCString &foundObj,
bool useEventLoop)
{
return findObject( remApp, remObj, remFun, data, foundApp, foundObj, useEventLoop, -1 );
}
bool DCOPClient::findObject(const QCString &remApp, const QCString &remObj,
const QCString &remFun, const QByteArray &data,
QCString &foundApp, QCString &foundObj,
bool useEventLoop, int timeout)
{
QCStringList appList;
QCString app = remApp;
if (app.isEmpty())
app = "*";
foundApp = 0;
foundObj = 0;
if (app[app.length()-1] == '*')
{
// Find all apps that match 'app'.
// NOTE: It would be more efficient to do the filtering in
// the dcopserver itself.
int len = app.length()-1;
QCStringList apps=registeredApplications();
for( QCStringList::ConstIterator it = apps.begin();
it != apps.end();
++it)
{
if ( strncmp( (*it).data(), app.data(), len) == 0)
appList.append(*it);
}
}
else
{
appList.append(app);
}
// We do all the local clients in phase1 and the rest in phase2
for(int phase=1; phase <= 2; phase++)
{
for( QCStringList::ConstIterator it = appList.begin();
it != appList.end();
++it)
{
QCString remApp = *it;
QCString replyType;
QByteArray replyData;
bool result = false;
DCOPClient *localClient = findLocalClient( remApp );
if ( (phase == 1) && localClient ) {
// In phase 1 we do all local clients
bool saveTransaction = d->transaction;
Q_INT32 saveTransactionId = d->transactionId;
QCString saveSenderId = d->senderId;
d->senderId = 0; // Local call
result = localClient->find( remApp, remObj, remFun, data, replyType, replyData );
Q_INT32 id = localClient->transactionId();
if (id) {
// Call delayed. We have to wait till it has been processed.
do {
QApplication::eventLoop()->processEvents( QEventLoop::WaitForMore);
} while( !localClient->isLocalTransactionFinished(id, replyType, replyData));
result = true;
}
d->transaction = saveTransaction;
d->transactionId = saveTransactionId;
d->senderId = saveSenderId;
}
else if ((phase == 2) && !localClient)
{
// In phase 2 we do the other clients
result = callInternal(remApp, remObj, remFun, data,
replyType, replyData, useEventLoop, timeout, DCOPFind);
}
if (result)
{
if (replyType == "DCOPRef")
{
DCOPRef ref;
QDataStream reply( replyData, IO_ReadOnly );
reply >> ref;
if (ref.app() == remApp) // Consistency check
{
// replyType contains objId.
foundApp = ref.app();
foundObj = ref.object();
return true;
}
}
}
}
}
return false;
}
bool DCOPClient::process(const QCString &, const QByteArray &,
QCString&, QByteArray &)
{
return false;
}
bool DCOPClient::isApplicationRegistered( const QCString& remApp)
{
QCString replyType;
QByteArray data, replyData;
QDataStream arg( data, IO_WriteOnly );
arg << remApp;
int result = false;
if ( call( "DCOPServer", "", "isApplicationRegistered(QCString)", data, replyType, replyData ) ) {
QDataStream reply( replyData, IO_ReadOnly );
reply >> result;
}
return result;
}
QCStringList DCOPClient::registeredApplications()
{
QCString replyType;
QByteArray data, replyData;
QCStringList result;
if ( call( "DCOPServer", "", "registeredApplications()", data, replyType, replyData ) ) {
QDataStream reply( replyData, IO_ReadOnly );
reply >> result;
}
return result;
}
QCStringList DCOPClient::remoteObjects( const QCString& remApp, bool *ok )
{
QCString replyType;
QByteArray data, replyData;
QCStringList result;
if ( ok )
*ok = false;
if ( call( remApp, "DCOPClient", "objects()", data, replyType, replyData ) ) {
QDataStream reply( replyData, IO_ReadOnly );
reply >> result;
if ( ok )
*ok = true;
}
return result;
}
QCStringList DCOPClient::remoteInterfaces( const QCString& remApp, const QCString& remObj, bool *ok )
{
QCString replyType;
QByteArray data, replyData;
QCStringList result;
if ( ok )
*ok = false;
if ( call( remApp, remObj, "interfaces()", data, replyType, replyData ) && replyType == "QCStringList") {
QDataStream reply( replyData, IO_ReadOnly );
reply >> result;
if ( ok )
*ok = true;
}
return result;
}
QCStringList DCOPClient::remoteFunctions( const QCString& remApp, const QCString& remObj, bool *ok )
{
QCString replyType;
QByteArray data, replyData;
QCStringList result;
if ( ok )
*ok = false;
if ( call( remApp, remObj, "functions()", data, replyType, replyData ) && replyType == "QCStringList") {
QDataStream reply( replyData, IO_ReadOnly );
reply >> result;
if ( ok )
*ok = true;
}
return result;
}
void DCOPClient::setNotifications(bool enabled)
{
QByteArray data;
QDataStream ds(data, IO_WriteOnly);
ds << static_cast<Q_INT8>(enabled);
QCString replyType;
QByteArray reply;
if (!call("DCOPServer", "", "setNotifications( bool )", data, replyType, reply))
qWarning("I couldn't enable notifications at the dcopserver!");
}
void DCOPClient::setDaemonMode( bool daemonMode )
{
QByteArray data;
QDataStream ds(data, IO_WriteOnly);
ds << static_cast<Q_INT8>( daemonMode );
QCString replyType;
QByteArray reply;
if (!call("DCOPServer", "", "setDaemonMode(bool)", data, replyType, reply))
qWarning("I couldn't enable daemon mode at the dcopserver!");
}
/*
DCOP <-> Qt bridge
********************************************************************************
*/
static void fillQtObjects( QCStringList& l, QObject* o, QCString path )
{
if ( !path.isEmpty() )
path += '/';
int unnamed = 0;
const QObjectList *list = o ? o->children() : QObject::objectTrees();
if ( list ) {
QObjectListIt it( *list );
QObject *obj;
while ( (obj=it.current()) ) {
++it;
QCString n = obj->name();
if ( n == "unnamed" || n.isEmpty() )
{
n.sprintf("%p", (void *) obj);
n = QString("unnamed%1(%2, %3)").arg(++unnamed).arg(obj->className()).arg(n).latin1();
}
QCString fn = path + n;
l.append( fn );
if ( obj->children() )
fillQtObjects( l, obj, fn );
}
}
}
namespace
{
struct O
{
O(): o(0) {}
O ( const QCString& str, QObject* obj ):s(str), o(obj){}
QCString s;
QObject* o;
};
} // namespace
static void fillQtObjectsEx( QValueList<O>& l, QObject* o, QCString path )
{
if ( !path.isEmpty() )
path += '/';
int unnamed = 0;
const QObjectList *list = o ? o->children() : QObject::objectTrees();
if ( list ) {
QObjectListIt it( *list );
QObject *obj;
while ( (obj=it.current()) ) {
++it;
QCString n = obj->name();
if ( n == "unnamed" || n.isEmpty() )
{
n.sprintf("%p", (void *) obj);
n = QString("unnamed%1(%2, %3)").arg(++unnamed).arg(obj->className()).arg(n).latin1();
}
QCString fn = path + n;
l.append( O( fn, obj ) );
if ( obj->children() )
fillQtObjectsEx( l, obj, fn );
}
}
}
static QObject* findQtObject( QCString id )
{
QRegExp expr( id );
QValueList<O> l;
fillQtObjectsEx( l, 0, "qt" );
// Prefer an exact match, but fall-back on the first that contains the substring
QObject* firstContains = 0L;
for ( QValueList<O>::ConstIterator it = l.begin(); it != l.end(); ++it ) {
if ( (*it).s == id ) // exact match
return (*it).o;
if ( !firstContains && (*it).s.contains( expr ) ) {
firstContains = (*it).o;
}
}
return firstContains;
}
static QCStringList findQtObjects( QCString id )
{
QRegExp expr( id );
QValueList<O> l;
fillQtObjectsEx( l, 0, "qt" );
QCStringList result;
for ( QValueList<O>::ConstIterator it = l.begin(); it != l.end(); ++it ) {
if ( (*it).s.contains( expr ) )
result << (*it).s;
}
return result;
}
static bool receiveQtObject( const QCString &objId, const QCString &fun, const QByteArray &data,
QCString& replyType, QByteArray &replyData)
{
if ( objId == "qt" ) {
if ( fun == "interfaces()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
l << "DCOPObject";
l << "Qt";
reply << l;
return true;
} else if ( fun == "functions()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
l << "QCStringList functions()";
l << "QCStringList interfaces()";
l << "QCStringList objects()";
l << "QCStringList find(QCString)";
reply << l;
return true;
} else if ( fun == "objects()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
fillQtObjects( l, 0, "qt" );
reply << l;
return true;
} else if ( fun == "find(QCString)" ) {
QDataStream ds( data, IO_ReadOnly );
QCString id;
ds >> id ;
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
reply << findQtObjects( id ) ;
return true;
}
} else if ( objId.left(3) == "qt/" ) {
QObject* o = findQtObject( objId );
if ( !o )
return false;
if ( fun == "functions()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
l << "QCStringList functions()";
l << "QCStringList interfaces()";
l << "QCStringList properties()";
l << "bool setProperty(QCString,QVariant)";
l << "QVariant property(QCString)";
QStrList lst = o->metaObject()->slotNames( true );
int i = 0;
for ( QPtrListIterator<char> it( lst ); it.current(); ++it ) {
if ( o->metaObject()->slot( i++, true )->access != QMetaData::Public )
continue;
QCString slot = it.current();
if ( slot.contains( "()" ) ) {
slot.prepend("void ");
l << slot;
}
}
reply << l;
return true;
} else if ( fun == "interfaces()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
QMetaObject *meta = o->metaObject();
while ( meta ) {
l.prepend( meta->className() );
meta = meta->superClass();
}
reply << l;
return true;
} else if ( fun == "properties()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
QStrList lst = o->metaObject()->propertyNames( true );
for ( QPtrListIterator<char> it( lst ); it.current(); ++it ) {
QMetaObject *mo = o->metaObject();
const QMetaProperty* p = mo->property( mo->findProperty( it.current(), true ), true );
if ( !p )
continue;
QCString prop = p->type();
prop += ' ';
prop += p->name();
if ( !p->writable() )
prop += " readonly";
l << prop;
}
reply << l;
return true;
} else if ( fun == "property(QCString)" ) {
replyType = "QVariant";
QDataStream ds( data, IO_ReadOnly );
QCString name;
ds >> name ;
QVariant result = o->property( name );
QDataStream reply( replyData, IO_WriteOnly );
reply << result;
return true;
} else if ( fun == "setProperty(QCString,QVariant)" ) {
QDataStream ds( data, IO_ReadOnly );
QCString name;
QVariant value;
ds >> name >> value;
replyType = "bool";
QDataStream reply( replyData, IO_WriteOnly );
reply << (Q_INT8) o->setProperty( name, value );
return true;
} else {
int slot = o->metaObject()->findSlot( fun, true );
if ( slot != -1 ) {
replyType = "void";
QUObject uo[ 1 ];
o->qt_invoke( slot, uo );
return true;
}
}
}
return false;
}
/*
********************************************************************************
End of DCOP <-> Qt bridge
*/
bool DCOPClient::receive(const QCString &/*app*/, const QCString &objId,
const QCString &fun, const QByteArray &data,
QCString& replyType, QByteArray &replyData)
{
d->transaction = false; // Assume no transaction.
if ( objId == "DCOPClient" ) {
if ( fun == "objects()" ) {
replyType = "QCStringList";
QDataStream reply( replyData, IO_WriteOnly );
QCStringList l;
if (d->qt_bridge_enabled)
{
l << "qt"; // the Qt bridge object
}
if ( kde_dcopObjMap ) {
QMap<QCString, DCOPObject *>::ConstIterator it( kde_dcopObjMap->begin());
for (; it != kde_dcopObjMap->end(); ++it) {
if ( !it.key().isEmpty() ) {
if ( it.key() == d->defaultObject )
l << "default";
l << it.key();
}
}
}
reply << l;
return true;
}
}
if ( objId.isEmpty() || objId == "DCOPClient" ) {
if ( fun == "applicationRegistered(QCString)" ) {
QDataStream ds( data, IO_ReadOnly );
QCString r;
ds >> r;
emit applicationRegistered( r );
return true;
} else if ( fun == "applicationRemoved(QCString)" ) {
QDataStream ds( data, IO_ReadOnly );
QCString r;
ds >> r;
emit applicationRemoved( r );
return true;
}
if ( process( fun, data, replyType, replyData ) )
return true;
// fall through and send to defaultObject if available
} else if (d->qt_bridge_enabled &&
(objId == "qt" || objId.left(3) == "qt/") ) { // dcop <-> qt bridge
return receiveQtObject( objId, fun, data, replyType, replyData );
}
if ( objId.isEmpty() || objId == "default" ) {
if ( !d->defaultObject.isEmpty() && DCOPObject::hasObject( d->defaultObject ) ) {
DCOPObject *objPtr = DCOPObject::find( d->defaultObject );
objPtr->setCallingDcopClient(this);
if (objPtr->process(fun, data, replyType, replyData))
return true;
}
// fall through and send to object proxies
}
if (!objId.isEmpty() && objId[objId.length()-1] == '*') {
// handle a multicast to several objects.
// doesn't handle proxies currently. should it?
QPtrList<DCOPObject> matchList =
DCOPObject::match(objId.left(objId.length()-1));
for (DCOPObject *objPtr = matchList.first();
objPtr != 0L; objPtr = matchList.next()) {
objPtr->setCallingDcopClient(this);
if (!objPtr->process(fun, data, replyType, replyData))
return false;
}
return true;
} else if (!DCOPObject::hasObject(objId)) {
if ( DCOPObjectProxy::proxies ) {
for ( QPtrListIterator<DCOPObjectProxy> it( *DCOPObjectProxy::proxies ); it.current(); ++it ) {
// TODO: it.current()->setCallingDcopClient(this);
if ( it.current()->process( objId, fun, data, replyType, replyData ) )
return true;
}
}
return false;
} else {
DCOPObject *objPtr = DCOPObject::find(objId);
objPtr->setCallingDcopClient(this);
if (!objPtr->process(fun, data, replyType, replyData)) {
// obj doesn't understand function or some other error.
return false;
}
}
return true;
}
// Check if the function result is a bool with the value "true"
// If so set the function result to DCOPRef pointing to (app,objId) and
// return true. Return false otherwise.
static bool findResultOk(QCString &replyType, QByteArray &replyData)
{
Q_INT8 success; // Tsk.. why is there no operator>>(bool)?
if (replyType != "bool") return false;
QDataStream reply( replyData, IO_ReadOnly );
reply >> success;
if (!success) return false;
return true;
}
// set the function result to DCOPRef pointing to (app,objId) and
// return true.
static bool findSuccess(const QCString &app, const QCString objId, QCString &replyType, QByteArray &replyData)
{
DCOPRef ref(app, objId);
replyType = "DCOPRef";
replyData = QByteArray();
QDataStream final_reply( replyData, IO_WriteOnly );
final_reply << ref;
return true;
}
bool DCOPClient::find(const QCString &app, const QCString &objId,
const QCString &fun, const QByteArray &data,
QCString& replyType, QByteArray &replyData)
{
d->transaction = false; // Transactions are not allowed.
if ( !app.isEmpty() && app != d->appId && app[app.length()-1] != '*') {
qWarning("WEIRD! we somehow received a DCOP message w/a different appId");
return false;
}
if (objId.isEmpty() || objId[objId.length()-1] != '*')
{
if (fun.isEmpty())
{
if (objId.isEmpty() || DCOPObject::hasObject(objId))
return findSuccess(app, objId, replyType, replyData);
return false;
}
// Message to application or single object...
if (receive(app, objId, fun, data, replyType, replyData))
{
if (findResultOk(replyType, replyData))
return findSuccess(app, objId, replyType, replyData);
}
}
else {
// handle a multicast to several objects.
// doesn't handle proxies currently. should it?
QPtrList<DCOPObject> matchList =
DCOPObject::match(objId.left(objId.length()-1));
for (DCOPObject *objPtr = matchList.first();
objPtr != 0L; objPtr = matchList.next())
{
replyType = 0;
replyData = QByteArray();
if (fun.isEmpty())
return findSuccess(app, objPtr->objId(), replyType, replyData);
objPtr->setCallingDcopClient(this);
if (objPtr->process(fun, data, replyType, replyData))
if (findResultOk(replyType, replyData))
return findSuccess(app, objPtr->objId(), replyType, replyData);
}
}
return false;
}
bool DCOPClient::call(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QByteArray &data,
QCString& replyType, QByteArray &replyData,
bool useEventLoop)
{
return call( remApp, remObjId, remFun, data, replyType, replyData, useEventLoop, -1 );
}
bool DCOPClient::call(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QByteArray &data,
QCString& replyType, QByteArray &replyData,
bool useEventLoop, int timeout)
{
if (remApp.isEmpty())
return false;
DCOPClient *localClient = findLocalClient( remApp );
if ( localClient ) {
bool saveTransaction = d->transaction;
Q_INT32 saveTransactionId = d->transactionId;
QCString saveSenderId = d->senderId;
d->senderId = 0; // Local call
bool b = localClient->receive( remApp, remObjId, remFun, data, replyType, replyData );
Q_INT32 id = localClient->transactionId();
if (id) {
// Call delayed. We have to wait till it has been processed.
do {
QApplication::eventLoop()->processEvents( QEventLoop::WaitForMore);
} while( !localClient->isLocalTransactionFinished(id, replyType, replyData));
b = true;
}
d->transaction = saveTransaction;
d->transactionId = saveTransactionId;
d->senderId = saveSenderId;
return b;
}
return callInternal(remApp, remObjId, remFun, data,
replyType, replyData, useEventLoop, timeout, DCOPCall);
}
void DCOPClient::asyncReplyReady()
{
while( d->asyncReplyQueue.count() )
{
ReplyStruct *replyStruct = d->asyncReplyQueue.take(0);
handleAsyncReply(replyStruct);
}
}
int DCOPClient::callAsync(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QByteArray &data,
QObject *callBackObj, const char *callBackSlot)
{
QCString replyType;
QByteArray replyData;
ReplyStruct *replyStruct = new ReplyStruct;
replyStruct->replyType = new QCString;
replyStruct->replyData = new QByteArray;
replyStruct->replyObject = callBackObj;
replyStruct->replySlot = callBackSlot;
replyStruct->replyId = ++d->transactionId;
if (d->transactionId < 0) // Ensure that ids > 0
d->transactionId = 0;
bool b = callInternal(remApp, remObjId, remFun, data,
replyStruct, false, -1, DCOPCall);
if (!b)
{
delete replyStruct->replyType;
delete replyStruct->replyData;
delete replyStruct;
return 0;
}
if (replyStruct->transactionId == 0)
{
// Call is finished already
QTimer::singleShot(0, this, SLOT(asyncReplyReady()));
d->asyncReplyQueue.append(replyStruct);
}
return replyStruct->replyId;
}
bool DCOPClient::callInternal(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QByteArray &data,
QCString& replyType, QByteArray &replyData,
bool useEventLoop, int timeout, int minor_opcode)
{
ReplyStruct replyStruct;
replyStruct.replyType = &replyType;
replyStruct.replyData = &replyData;
return callInternal(remApp, remObjId, remFun, data, &replyStruct, useEventLoop, timeout, minor_opcode);
}
bool DCOPClient::callInternal(const QCString &remApp, const QCString &remObjId,
const QCString &remFun, const QByteArray &data,
ReplyStruct *replyStruct,
bool useEventLoop, int timeout, int minor_opcode)
{
if ( !isAttached() )
return false;
DCOPMsg *pMsg;
CARD32 oldCurrentKey = d->currentKey;
if ( !d->currentKey )
d->currentKey = d->key; // no key yet, initiate new call
QByteArray ba;
QDataStream ds(ba, IO_WriteOnly);
ds << d->appId << remApp << remObjId << normalizeFunctionSignature(remFun) << data.size();
IceGetHeader(d->iceConn, d->majorOpcode, minor_opcode,
sizeof(DCOPMsg), DCOPMsg, pMsg);
pMsg->key = d->currentKey;
int datalen = ba.size() + data.size();
pMsg->length += datalen;
// qWarning("DCOP: %s made call %s:%s:%s key = %d", d->appId.data(), remApp.data(), remObjId.data(), remFun.data(), pMsg->key);
IceSendData(d->iceConn, ba.size(), const_cast<char *>(ba.data()));
IceSendData(d->iceConn, data.size(), const_cast<char *>(data.data()));
if (IceConnectionStatus(d->iceConn) != IceConnectAccepted)
return false;
IceFlush (d->iceConn);
IceReplyWaitInfo waitInfo;
waitInfo.sequence_of_request = IceLastSentSequenceNumber(d->iceConn);
waitInfo.major_opcode_of_request = d->majorOpcode;
waitInfo.minor_opcode_of_request = minor_opcode;
replyStruct->transactionId = -1;
waitInfo.reply = static_cast<IcePointer>(replyStruct);
Bool readyRet = False;
IceProcessMessagesStatus s;
timeval time_start;
int time_left = -1;
if( timeout >= 0 )
{
gettimeofday( &time_start, NULL );
time_left = timeout;
}
for(;;) {
bool checkMessages = true;
if ( useEventLoop
? d->notifier != NULL // useEventLoop needs a socket notifier and a qApp
: timeout >= 0 ) { // !useEventLoop doesn't block only for timeout >= 0
const int guiTimeout = 100;
checkMessages = false;
int msecs = useEventLoop
? guiTimeout // timeout for the GUI refresh
: time_left; // time remaining for the whole call
fd_set fds;
struct timeval tv;
FD_ZERO( &fds );
FD_SET( socket(), &fds );
tv.tv_sec = msecs / 1000;
tv.tv_usec = (msecs % 1000) * 1000;
if ( select( socket() + 1, &fds, 0, 0, &tv ) <= 0 ) {
if( useEventLoop && (timeout < 0 || time_left > guiTimeout)) {
// nothing was available, we got a timeout. Reactivate
// the GUI in blocked state.
bool old_lock = d->non_blocking_call_lock;
if ( !old_lock ) {
d->non_blocking_call_lock = true;
emit blockUserInput( true );
}
if( timeout >= 0 )
d->eventLoopTimer.start(time_left - guiTimeout, true);
qApp->enter_loop();
d->eventLoopTimer.stop();
if ( !old_lock ) {
d->non_blocking_call_lock = false;
emit blockUserInput( false );
}
}
}
else
{
checkMessages = true;
}
}
if (!d->iceConn)
return false;
if( replyStruct->transactionId != -1 )
{
if (replyStruct->transactionId == 0)
break; // Call complete
if (!replyStruct->replySlot.isEmpty())
break; // Async call
}
if( checkMessages ) { // something is available
s = IceProcessMessages(d->iceConn, &waitInfo,
&readyRet);
if (s == IceProcessMessagesIOError) {
detach();
d->currentKey = oldCurrentKey;
return false;
}
}
if( replyStruct->transactionId != -1 )
{
if (replyStruct->transactionId == 0)
break; // Call complete
if (!replyStruct->replySlot.isEmpty())
break; // Async call
}
if( timeout < 0 )
continue;
timeval time_now;
gettimeofday( &time_now, NULL );
time_left = timeout -
((time_now.tv_sec - time_start.tv_sec) * 1000) -
((time_now.tv_usec - time_start.tv_usec) / 1000);
if( time_left <= 0)
{
if (useEventLoop)
{
// Before we fail, check one more time if something is available
time_left = 0;
useEventLoop = false;
continue;
}
*(replyStruct->replyType) = QCString();
*(replyStruct->replyData) = QByteArray();
replyStruct->status = ReplyStruct::Failed;
break;
}
}
// Wake up parent call, maybe it's reply is available already.
if ( d->non_blocking_call_lock ) {
qApp->exit_loop();
}
d->currentKey = oldCurrentKey;
return replyStruct->status != ReplyStruct::Failed;
}
void DCOPClient::eventLoopTimeout()
{
qApp->exit_loop();
}
void DCOPClient::processSocketData(int fd)
{
// Make sure there is data to read!
fd_set fds;
timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(fd, &fds);
int result = select(fd+1, &fds, 0, 0, &timeout);
if (result == 0)
return;
if ( d->non_blocking_call_lock ) {
if( qApp )
qApp->exit_loop();
return;
}
if (!d->iceConn) {
if( d->notifier )
d->notifier->deleteLater();
d->notifier = 0;
qWarning("received an error processing data from the DCOP server!");
return;
}
IceProcessMessagesStatus s = IceProcessMessages(d->iceConn, 0, 0);
if (s == IceProcessMessagesIOError) {
detach();
qWarning("received an error processing data from the DCOP server!");
return;
}
}
void DCOPClient::setDefaultObject( const QCString& objId )
{
d->defaultObject = objId;
}
QCString DCOPClient::defaultObject() const
{
return d->defaultObject;
}
bool
DCOPClient::isLocalTransactionFinished(Q_INT32 id, QCString &replyType, QByteArray &replyData)
{
DCOPClientPrivate::LocalTransactionResult *result = d->localTransActionList.take(id);
if (!result)
return false;
replyType = result->replyType;
replyData = result->replyData;
delete result;
return true;
}
DCOPClientTransaction *
DCOPClient::beginTransaction()
{
if (d->opcode == DCOPSend)
return 0;
if (!d->transactionList)
d->transactionList = new QPtrList<DCOPClientTransaction>;
d->transaction = true;
DCOPClientTransaction *trans = new DCOPClientTransaction();
trans->senderId = d->senderId;
trans->id = ++d->transactionId;
if (d->transactionId < 0) // Ensure that ids > 0
d->transactionId = 0;
trans->key = d->currentKey;
d->transactionList->append( trans );
return trans;
}
Q_INT32
DCOPClient::transactionId() const
{
if (d->transaction)
return d->transactionId;
else
return 0;
}
void
DCOPClient::endTransaction( DCOPClientTransaction *trans, QCString& replyType,
QByteArray &replyData)
{
if ( !trans )
return;
if ( !isAttached() )
return;
if ( !d->transactionList) {
qWarning("Transaction unknown: No pending transactions!");
return; // No pending transactions!
}
if ( !d->transactionList->removeRef( trans ) ) {
qWarning("Transaction unknown: Not on list of pending transactions!");
return; // Transaction
}
if (trans->senderId.isEmpty())
{
// Local transaction
DCOPClientPrivate::LocalTransactionResult *result = new DCOPClientPrivate::LocalTransactionResult();
result->replyType = replyType;
result->replyData = replyData;
d->localTransActionList.insert(trans->id, result);
delete trans;
return;
}
DCOPMsg *pMsg;
QByteArray ba;
QDataStream ds(ba, IO_WriteOnly);
ds << d->appId << trans->senderId << trans->id << replyType << replyData;
IceGetHeader(d->iceConn, d->majorOpcode, DCOPReplyDelayed,
sizeof(DCOPMsg), DCOPMsg, pMsg);
pMsg->key = trans->key;
pMsg->length += ba.size();
IceSendData( d->iceConn, ba.size(), const_cast<char *>(ba.data()) );
delete trans;
}
void
DCOPClient::emitDCOPSignal( const QCString &object, const QCString &signal, const QByteArray &data)
{
// We hack the sending object name into the signal name
send("DCOPServer", "emit", object+"#"+normalizeFunctionSignature(signal), data);
}
void
DCOPClient::emitDCOPSignal( const QCString &signal, const QByteArray &data)
{
emitDCOPSignal(0, signal, data);
}
bool
DCOPClient::connectDCOPSignal( const QCString &sender, const QCString &senderObj,
const QCString &signal,
const QCString &receiverObj, const QCString &slot, bool Volatile)
{
QCString replyType;
QByteArray data, replyData;
Q_INT8 iVolatile = Volatile ? 1 : 0;
QDataStream args(data, IO_WriteOnly );
args << sender << senderObj << normalizeFunctionSignature(signal) << receiverObj << normalizeFunctionSignature(slot) << iVolatile;
if (!call("DCOPServer", 0,
"connectSignal(QCString,QCString,QCString,QCString,QCString,bool)",
data, replyType, replyData))
{
return false;
}
if (replyType != "bool")
return false;
QDataStream reply(replyData, IO_ReadOnly );
Q_INT8 result;
reply >> result;
return (result != 0);
}
bool
DCOPClient::connectDCOPSignal( const QCString &sender, const QCString &signal,
const QCString &receiverObj, const QCString &slot, bool Volatile)
{
return connectDCOPSignal( sender, 0, signal, receiverObj, slot, Volatile);
}
bool
DCOPClient::disconnectDCOPSignal( const QCString &sender, const QCString &senderObj,
const QCString &signal,
const QCString &receiverObj, const QCString &slot)
{
QCString replyType;
QByteArray data, replyData;
QDataStream args(data, IO_WriteOnly );
args << sender << senderObj << normalizeFunctionSignature(signal) << receiverObj << normalizeFunctionSignature(slot);
if (!call("DCOPServer", 0,
"disconnectSignal(QCString,QCString,QCString,QCString,QCString)",
data, replyType, replyData))
{
return false;
}
if (replyType != "bool")
return false;
QDataStream reply(replyData, IO_ReadOnly );
Q_INT8 result;
reply >> result;
return (result != 0);
}
bool
DCOPClient::disconnectDCOPSignal( const QCString &sender, const QCString &signal,
const QCString &receiverObj, const QCString &slot)
{
return disconnectDCOPSignal( sender, 0, signal, receiverObj, slot);
}
void
DCOPClient::setPriorityCall(bool b)
{
if (b)
{
if (d->currentKey == 2)
return;
d->currentKeySaved = d->currentKey;
d->currentKey = 2;
}
else
{
if (d->currentKey != 2)
return;
d->currentKey = d->currentKeySaved;
if ( !d->messages.isEmpty() )
d->postMessageTimer.start( 0, true ); // Process queued messages
}
}
void
DCOPClient::emergencyClose()
{
QPtrList<DCOPClient> list;
client_map_t *map = DCOPClient_CliMap;
if (!map) return;
QAsciiDictIterator<DCOPClient> it(*map);
while(it.current()) {
list.removeRef(it.current());
list.append(it.current());
++it;
}
for(DCOPClient *cl = list.first(); cl; cl = list.next())
{
if (cl->d->iceConn) {
IceProtocolShutdown(cl->d->iceConn, cl->d->majorOpcode);
IceCloseConnection(cl->d->iceConn);
cl->d->iceConn = 0L;
}
}
}
const char *
DCOPClient::postMortemSender()
{
if (!dcop_main_client)
return "";
if (dcop_main_client->d->senderId.isEmpty())
return "";
return dcop_main_client->d->senderId.data();
}
const char *
DCOPClient::postMortemObject()
{
if (!dcop_main_client)
return "";
return dcop_main_client->d->objId.data();
}
const char *
DCOPClient::postMortemFunction()
{
if (!dcop_main_client)
return "";
return dcop_main_client->d->function.data();
}
void DCOPClient::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
#include <dcopclient.moc>
DCOPClient::applicationRemoved
void applicationRemoved(const QCString &appId)
Indicates that the formerly registered application appId has been removed.
DCOPClient::setDefaultObject
void setDefaultObject(const QCString &objId)
Installs object objId as application-wide default object.
Definition: dcopclient.cpp:2023
DCOPClient::defaultObject
QCString defaultObject() const
Returns the current default object or an empty string if no object is installed as default object...
Definition: dcopclient.cpp:2029
DCOPClient::iceauthPath
static QCString iceauthPath()
Return the path of iceauth or an empty string if not found.
Definition: dcopclient.cpp:213
DCOPClient::acceptCalls
bool acceptCalls() const
Returns whether the client handles incoming calls.
Definition: dcopclient.cpp:921
DCOPClient::mainClient
static DCOPClient * mainClient()
Returns the application's main dcop client.
Definition: dcopclient.cpp:583
DCOPClient::attach
bool attach()
Attaches to the DCOP server.
Definition: dcopclient.cpp:663
DCOPClient::setAcceptCalls
void setAcceptCalls(bool b)
Specify whether the client should accept incoming calls.
Definition: dcopclient.cpp:926
DCOPClient::DCOPClient
DCOPClient()
Constructs a new DCOP client, but does not attach to any server.
Definition: dcopclient.cpp:594
DCOPRef
A DCOPRef(erence) encapsulates a remote DCOP object as a triple <app,obj,type> where type is optional...
Definition: dcopref.h:278
DCOPClient::setQtBridgeEnabled
void setQtBridgeEnabled(bool b)
Specify whether Qt objects of the application should be accessible via DCOP.
Definition: dcopclient.cpp:937
DCOPClient::~DCOPClient
virtual ~DCOPClient()
Cleans up any open connections and dynamic data.
Definition: dcopclient.cpp:619
DCOPClient::qtBridgeEnabled
bool qtBridgeEnabled()
Returns whether the DCOP - Qt bridge is enabled.
Definition: dcopclient.cpp:932
DCOPClient::normalizeFunctionSignature
static QCString normalizeFunctionSignature(const QCString &fun)
Normalizes the function signature fun.
Definition: dcopclient.cpp:1014
DCOPClient
Inter-process communication and remote procedure calls for KDE applications.
Definition: dcopclient.h:68
DCOPClient::beginTransaction
DCOPClientTransaction * beginTransaction()
Delays the reply of the current function call until endTransaction() is called.
Definition: dcopclient.cpp:2049
DCOPClient::isRegistered
bool isRegistered() const
Returns whether or not the client is registered at the server.
Definition: dcopclient.cpp:989
DCOPClient::remoteObjects
QCStringList remoteObjects(const QCString &remApp, bool *ok=0)
Retrieves the list of objects of the remote application remApp.
Definition: dcopclient.cpp:1247
DCOPClient::endTransaction
void endTransaction(DCOPClientTransaction *t, QCString &replyType, QByteArray &replyData)
Sends the delayed reply of a function call.
Definition: dcopclient.cpp:2079
DCOPClient::send
bool send(const QCString &remApp, const QCString &remObj, const QCString &remFun, const QByteArray &data)
Sends a data block to the server.
Definition: dcopclient.cpp:1048
DCOPClient::senderId
QCString senderId() const
Returns the appId of the last application that talked to us.
Definition: dcopclient.cpp:1042
DCOPClient::appId
QCString appId() const
Returns the current app id or a null string if the application hasn't yet been registered.
Definition: dcopclient.cpp:995
DCOPClient::processSocketData
void processSocketData(int socknum)
Process data from the socket.
Definition: dcopclient.cpp:1987
DCOPClient::remoteFunctions
QCStringList remoteFunctions(const QCString &remApp, const QCString &remObj, bool *ok=0)
Retrieves the list of functions of the remote object remObj of application remApp.
Definition: dcopclient.cpp:1279
DCOPClient::registerAs
QCString registerAs(const QCString &appId, bool addPID=true)
Registers at the DCOP server.
Definition: dcopclient.cpp:942
DCOPClient::attachFailed
void attachFailed(const QString &msg)
Indicates that the process of establishing DCOP communications failed in some manner.
DCOPClient::dcopServerFile
static QCString dcopServerFile(const QCString &hostname=0)
File with information how to reach the dcopserver.
Definition: dcopclient.cpp:300
DCOPClient::setServerAddress
static void setServerAddress(const QCString &addr)
Sets the address of a server to use upon attaching.
Definition: dcopclient.cpp:655
DCOPClient::connectDCOPSignal
bool connectDCOPSignal(const QCString &sender, const QCString &senderObj, const QCString &signal, const QCString &receiverObj, const QCString &slot, bool Volatile)
Connects to a DCOP signal.
Definition: dcopclient.cpp:2142
DCOPClient::disconnectDCOPSignal
bool disconnectDCOPSignal(const QCString &sender, const QCString &senderObj, const QCString &signal, const QCString &receiverObj, const QCString &slot)
Disconnects a DCOP signal.
Definition: dcopclient.cpp:2177
DCOPClient::registeredApplications
QCStringList registeredApplications()
Retrieves the list of all currently registered applications from dcopserver.
Definition: dcopclient.cpp:1235
DCOPClient::findLocalClient
static DCOPClient * findLocalClient(const QCString &_appId)
Look for the given client only in this process.
Definition: dcopclient.cpp:98
DCOPObject::match
static QPtrList< DCOPObject > match(const QCString &partialId)
Tries to find an object using a partial object id.
Definition: dcopobject.cpp:135
DCOPClient::transactionId
Q_INT32 transactionId() const
Test whether the current function call is delayed.
Definition: dcopclient.cpp:2070
DCOPClient::setMainClient
static void setMainClient(DCOPClient *mainClient)
Sets the application's main dcop client.
Definition: dcopclient.cpp:588
DCOPClient::emitDCOPSignal
void emitDCOPSignal(const QCString &object, const QCString &signal, const QByteArray &data)
Emits signal as DCOP signal from object object with data as arguments.
Definition: dcopclient.cpp:2129
DCOPClient::suspend
void suspend()
Temporarily suspends processing of DCOP events.
Definition: dcopclient.cpp:685
DCOPClient::detach
bool detach()
Detaches from the DCOP server.
Definition: dcopclient.cpp:885
KStdAccel::key
int key(StdAccel) KDE_DEPRECATED
DCOPClient::applicationRegistered
void applicationRegistered(const QCString &appId)
Indicates that the application appId has been registered with the server we are attached to...
DCOPObject::find
static DCOPObject * find(const QCString &objId)
Try to find a dcop object with the given id.
Definition: dcopobject.cpp:125
DCOPClient::callAsync
int callAsync(const QCString &remApp, const QCString &remObj, const QCString &remFun, const QByteArray &data, QObject *callBackObj, const char *callBackSlot)
Performs a asynchronous send with receive callback.
Definition: dcopclient.cpp:1780
DCOPClient::setDaemonMode
void setDaemonMode(bool daemonMode)
Tells the dcopserver to treat the client as daemon client, not as regular client. ...
Definition: dcopclient.cpp:1307
DCOPClient::setNotifications
void setNotifications(bool enabled)
Enables / disables the applicationRegistered() / applicationRemoved() signals.
Definition: dcopclient.cpp:1295
DCOPClient::isAttachedToForeignServer
bool isAttachedToForeignServer() const
Returns whether the client is attached to a server owned by another user.
Definition: dcopclient.cpp:916
DCOPRef::app
QCString app() const
Name of the application in which the object resides.
Definition: dcopref.cpp:140
DCOPClient::call
bool call(const QCString &remApp, const QCString &remObj, const QCString &remFun, const QByteArray &data, QCString &replyType, QByteArray &replyData, bool useEventLoop, int timeout)
Performs a synchronous send and receive.
Definition: dcopclient.cpp:1736
DCOPClient::remoteInterfaces
QCStringList remoteInterfaces(const QCString &remApp, const QCString &remObj, bool *ok=0)
Retrieves the list of interfaces of the remote object remObj of application remApp.
Definition: dcopclient.cpp:1263
DCOPClient::resume
void resume()
Resumes the processing of DCOP events.
Definition: dcopclient.cpp:695
DCOPClient::dcopServerFileOld
static QCString dcopServerFileOld(const QCString &hostname=0) KDE_DEPRECATED
Definition: dcopclient.cpp:307
DCOPClient::findObject
bool findObject(const QCString &remApp, const QCString &remObj, const QCString &remFun, const QByteArray &data, QCString &foundApp, QCString &foundObj, bool useEventLoop, int timeout)
Searches for an object which matches a criteria.
Definition: dcopclient.cpp:1119
DCOPClient::isAttached
bool isAttached() const
Returns whether or not the client is attached to the server.
Definition: dcopclient.cpp:908
DCOPObject::hasObject
static bool hasObject(const QCString &objId)
Checks whether an object with the given id is known in this process.
Definition: dcopobject.cpp:117
DCOPClient::process
virtual bool process(const QCString &fun, const QByteArray &data, QCString &replyType, QByteArray &replyData)
Reimplement this function to handle app-wide function calls unassociated w/an object.
Definition: dcopclient.cpp:1215
DCOPClient::isSuspended
bool isSuspended() const
Returns whether DCOP events are being processed.
Definition: dcopclient.cpp:705
DCOPObject
Provides an interface for receiving DCOP messages.
Definition: dcopobject.h:67
DCOPObject::process
virtual bool process(const QCString &fun, const QByteArray &data, QCString &replyType, QByteArray &replyData)
Dispatches a message.
Definition: dcopobject.cpp:166
DCOPClient::socket
int socket() const
Returns the socket fd that is used for communication with the server.
Definition: dcopclient.cpp:1001
DCOPClient::blockUserInput
void blockUserInput(bool block)
Indicates that user input shall be blocked or released, depending on the argument.
DCOPClient::isApplicationRegistered
bool isApplicationRegistered(const QCString &remApp)
Checks whether remApp is registered with the DCOP server.
Definition: dcopclient.cpp:1221
DCOPObject::objId
QCString objId() const
Returns the object id of the DCOPObject.
Definition: dcopobject.cpp:112
DCOPReply
Represents the return value of a DCOPRef:call() or DCOPRef:send() invocation.
Definition: dcopref.h:44
KStdAccel::name
QString name(StdAccel id)

KDE Home | KDE Accessibility Home | Description of Access Keys