dcopclient.cpp

00001 /*****************************************************************
00002 
00003 Copyright (c) 1999 Preston Brown <pbrown@kde.org>
00004 Copyright (c) 1999 Matthias Ettrich <ettrich@kde.org>
00005 
00006 Permission is hereby granted, free of charge, to any person obtaining a copy
00007 of this software and associated documentation files (the "Software"), to deal
00008 in the Software without restriction, including without limitation the rights
00009 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00010 copies of the Software, and to permit persons to whom the Software is
00011 furnished to do so, subject to the following conditions:
00012 
00013 The above copyright notice and this permission notice shall be included in
00014 all copies or substantial portions of the Software.
00015 
00016 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00019 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
00020 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00021 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00022 
00023 ******************************************************************/
00024 
00025 // qt <-> dcop integration
00026 #include <qobjectlist.h>
00027 #include <qmetaobject.h>
00028 #include <qvariant.h>
00029 #include <qtimer.h>
00030 #include <qintdict.h>
00031 #include <qeventloop.h>
00032 // end of qt <-> dcop integration
00033 
00034 #include "config.h"
00035 
00036 #include <config.h>
00037 #include <dcopref.h>
00038 
00039 #include <sys/time.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #include <sys/file.h>
00043 #include <sys/socket.h>
00044 
00045 #include <ctype.h>
00046 #include <unistd.h>
00047 #include <stdlib.h>
00048 #include <assert.h>
00049 #include <string.h>
00050 
00051 #ifndef QT_CLEAN_NAMESPACE
00052 #define QT_CLEAN_NAMESPACE
00053 #endif
00054 #include <qguardedptr.h>
00055 #include <qtextstream.h>
00056 #include <qfile.h>
00057 #include <qdir.h>
00058 #include <qapplication.h>
00059 #include <qsocketnotifier.h>
00060 #include <qregexp.h>
00061 
00062 #include <private/qucomextra_p.h>
00063 
00064 #include <dcopglobal.h>
00065 #include <dcopclient.h>
00066 #include <dcopobject.h>
00067 
00068 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00069 #include <X11/Xmd.h> 
00070 #endif
00071 extern "C" {
00072 #include <KDE-ICE/ICElib.h>
00073 #include <KDE-ICE/ICEutil.h>
00074 #include <KDE-ICE/ICEmsg.h>
00075 #include <KDE-ICE/ICEproto.h>
00076 }
00077 
00078 // #define DCOPCLIENT_DEBUG 1
00079 
00080 extern QMap<QCString, DCOPObject *> * kde_dcopObjMap; // defined in dcopobject.cpp
00081 
00082 /*********************************************
00083  * Keep track of local clients
00084  *********************************************/
00085 typedef QAsciiDict<DCOPClient> client_map_t;
00086 static client_map_t *DCOPClient_CliMap = 0;
00087 
00088 static
00089 client_map_t *cliMap()
00090 {
00091     if (!DCOPClient_CliMap)
00092         DCOPClient_CliMap = new client_map_t;
00093     return DCOPClient_CliMap;
00094 }
00095 
00096 DCOPClient *DCOPClient::findLocalClient( const QCString &_appId )
00097 {
00098     return cliMap()->find(_appId.data());
00099 }
00100 
00101 static
00102 void registerLocalClient( const QCString &_appId, DCOPClient *client )
00103 {
00104     cliMap()->replace(_appId.data(), client);
00105 }
00106 
00107 static
00108 void unregisterLocalClient( const QCString &_appId )
00109 {
00110     client_map_t *map = cliMap();
00111     map->remove(_appId.data());
00112 }
00114 
00115 template class QPtrList<DCOPObjectProxy>;
00116 template class QPtrList<DCOPClientTransaction>;
00117 template class QPtrList<_IceConn>;
00118 
00119 struct DCOPClientMessage
00120 {
00121     int opcode;
00122     CARD32 key;
00123     QByteArray data;
00124 };
00125 
00126 class DCOPClient::ReplyStruct
00127 {
00128 public:
00129     enum ReplyStatus { Pending, Ok, Failed };
00130     ReplyStruct() {
00131         status = Pending;
00132         replyType = 0;
00133         replyData = 0;
00134         replyId = -1;
00135         transactionId = -1;
00136         replyObject = 0;
00137     }
00138     ReplyStatus status;
00139     QCString* replyType;
00140     QByteArray* replyData;
00141     int replyId;
00142     Q_INT32 transactionId;
00143     QCString calledApp;
00144     QGuardedPtr<QObject> replyObject;
00145     QCString replySlot;
00146 };
00147 
00148 class DCOPClientPrivate
00149 {
00150 public:
00151     DCOPClient *parent;
00152     QCString appId;
00153     IceConn iceConn;
00154     int majorOpcode; // major opcode negotiated w/server and used to tag all comms.
00155 
00156     int majorVersion, minorVersion; // protocol versions negotiated w/server
00157 
00158     static const char* serverAddr; // location of server in ICE-friendly format.
00159     QSocketNotifier *notifier;
00160     bool non_blocking_call_lock;
00161     bool registered;
00162     bool foreign_server;
00163     bool accept_calls;
00164     bool accept_calls_override; // If true, user has specified policy.
00165     bool qt_bridge_enabled;
00166 
00167     QCString senderId;
00168     QCString objId;
00169     QCString function;
00170 
00171     QCString defaultObject;
00172     QPtrList<DCOPClientTransaction> *transactionList;
00173     bool transaction;
00174     Q_INT32 transactionId;
00175     int opcode;
00176 
00177     // Special key values:
00178     // 0 : Not specified
00179     // 1 : DCOPSend
00180     // 2 : Priority
00181     // >= 42: Normal
00182     CARD32 key;
00183     CARD32 currentKey; 
00184     CARD32 currentKeySaved;
00185 
00186     QTimer postMessageTimer;
00187     QPtrList<DCOPClientMessage> messages;
00188 
00189     QPtrList<DCOPClient::ReplyStruct> pendingReplies;
00190     QPtrList<DCOPClient::ReplyStruct> asyncReplyQueue;
00191 
00192     struct LocalTransactionResult 
00193     {
00194         QCString replyType;
00195         QByteArray replyData;
00196     };
00197 
00198     QIntDict<LocalTransactionResult> localTransActionList;
00199     
00200     QTimer eventLoopTimer;
00201 };
00202 
00203 class DCOPClientTransaction
00204 {
00205 public:
00206     Q_INT32 id;
00207     CARD32 key;
00208     QCString senderId;
00209 };
00210 
00211 QCString DCOPClient::iceauthPath()
00212 {
00213 #ifdef Q_OS_WIN32
00214     char    szPath[512];
00215     char *  pszFilePart;
00216     int     ret;
00217     ret = SearchPathA(NULL,"iceauth.exe",NULL,sizeof(szPath)/sizeof(szPath[0]),szPath,&pszFilePart);
00218     if(ret != 0)
00219         return QCString(szPath);
00220 #else
00221     QCString path = ::getenv("PATH");
00222     if (path.isEmpty())
00223         path = "/bin:/usr/bin";
00224     path += ":/usr/bin/X11:/usr/X11/bin:/usr/X11R6/bin";
00225     QCString fPath = strtok(path.data(), ":\b");
00226     while (!fPath.isNull())
00227     {
00228         fPath += "/iceauth";
00229         if (access(fPath.data(), X_OK) == 0)
00230         {
00231             return fPath;
00232         }
00233    
00234         fPath = strtok(NULL, ":\b");
00235     }
00236 #endif
00237     return 0;
00238 }
00239 
00240 static QCString dcopServerFile(const QCString &hostname, bool old)
00241 {
00242     QCString fName = ::getenv("DCOPAUTHORITY");
00243     if (!old && !fName.isEmpty())
00244         return fName;
00245 
00246     fName = QFile::encodeName( QDir::homeDirPath() );
00247 //    fName = ::getenv("HOME");
00248     if (fName.isEmpty())
00249     {
00250         fprintf(stderr, "Aborting. $HOME is not set.\n");
00251         exit(1);
00252     }
00253 #ifdef Q_WS_X11
00254     QCString disp = getenv("DISPLAY");
00255 #elif defined(Q_WS_QWS)
00256     QCString disp = getenv("QWS_DISPLAY");
00257 #else
00258     QCString disp;
00259 #endif
00260     if (disp.isEmpty())
00261         disp = "NODISPLAY";
00262 
00263     int i;
00264     if((i = disp.findRev('.')) > disp.findRev(KPATH_SEPARATOR) && i >= 0)
00265         disp.truncate(i);
00266 
00267     if (!old)
00268     {
00269         while( (i = disp.find(KPATH_SEPARATOR)) >= 0)
00270             disp[i] = '_';
00271     }
00272 
00273     fName += "/.DCOPserver_";
00274     if (hostname.isEmpty())
00275     {
00276         char hostName[256];
00277         hostName[0] = '\0';
00278         if (gethostname(hostName, sizeof(hostName)))
00279         {
00280             fName += "localhost";
00281         }
00282         else 
00283         {
00284             hostName[sizeof(hostName)-1] = '\0';
00285             fName += hostName;
00286         }
00287     }
00288     else
00289     {
00290         fName += hostname;
00291     }
00292     fName += "_"+disp;
00293     return fName;
00294 }
00295 
00296 
00297 // static
00298 QCString DCOPClient::dcopServerFile(const QCString &hostname)
00299 {
00300     return ::dcopServerFile(hostname, false);
00301 }
00302 
00303 
00304 // static
00305 QCString DCOPClient::dcopServerFileOld(const QCString &hostname)
00306 {
00307     return ::dcopServerFile(hostname, true);
00308 }
00309 
00310 
00311 const char* DCOPClientPrivate::serverAddr = 0;
00312 
00313 static void DCOPProcessInternal( DCOPClientPrivate *d, int opcode, CARD32 key, const QByteArray& dataReceived, bool canPost  );
00314 
00315 void DCOPClient::handleAsyncReply(ReplyStruct *replyStruct)
00316 {
00317     if (replyStruct->replyObject)
00318     {
00319         QObject::connect(this, SIGNAL(callBack(int, const QCString&, const QByteArray &)),
00320                replyStruct->replyObject, replyStruct->replySlot);
00321         emit callBack(replyStruct->replyId, *(replyStruct->replyType), *(replyStruct->replyData));
00322         QObject::disconnect(this, SIGNAL(callBack(int, const QCString&, const QByteArray &)),
00323                replyStruct->replyObject, replyStruct->replySlot);
00324     }
00325     delete replyStruct;
00326 }
00327 
00331 static void DCOPProcessMessage(IceConn iceConn, IcePointer clientObject,
00332                         int opcode, unsigned long length, Bool /*swap*/,
00333                         IceReplyWaitInfo *replyWait,
00334                         Bool *replyWaitRet)
00335 {
00336     DCOPMsg *pMsg = 0;
00337     DCOPClientPrivate *d = static_cast<DCOPClientPrivate *>(clientObject);
00338     DCOPClient::ReplyStruct *replyStruct = replyWait ? static_cast<DCOPClient::ReplyStruct*>(replyWait->reply) : 0;
00339 
00340     IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00341     CARD32 key = pMsg->key;
00342     if ( d->key == 0 )
00343         d->key = key; // received a key from the server
00344 
00345     QByteArray dataReceived( length );
00346     IceReadData(iceConn, length, dataReceived.data() );
00347 
00348     d->opcode = opcode;
00349     switch (opcode ) {
00350 
00351     case DCOPReplyFailed:
00352         if ( replyStruct ) {
00353             replyStruct->status = DCOPClient::ReplyStruct::Failed;
00354             replyStruct->transactionId = 0;
00355             *replyWaitRet = True;
00356             return;
00357         } else {
00358             qWarning("Very strange! got a DCOPReplyFailed opcode, but we were not waiting for a reply!");
00359             return;
00360         }
00361     case DCOPReply:
00362         if ( replyStruct ) {
00363             QByteArray* b = replyStruct->replyData;
00364             QCString* t =  replyStruct->replyType;
00365             replyStruct->status = DCOPClient::ReplyStruct::Ok;
00366             replyStruct->transactionId = 0;
00367 
00368             QCString calledApp, app;
00369             QDataStream ds( dataReceived, IO_ReadOnly );
00370             ds >> calledApp >> app >> *t >> *b;
00371 
00372             *replyWaitRet = True;
00373             return;
00374         } else {
00375             qWarning("Very strange! got a DCOPReply opcode, but we were not waiting for a reply!");
00376             return;
00377         }
00378     case DCOPReplyWait:
00379         if ( replyStruct ) {
00380             QCString calledApp, app;
00381             Q_INT32 id;
00382             QDataStream ds( dataReceived, IO_ReadOnly );
00383             ds >> calledApp >> app >> id;
00384             replyStruct->transactionId = id;
00385             replyStruct->calledApp = calledApp;
00386             d->pendingReplies.append(replyStruct);
00387             *replyWaitRet = True;
00388             return;
00389         } else {
00390             qWarning("Very strange! got a DCOPReplyWait opcode, but we were not waiting for a reply!");
00391             return;
00392         }
00393     case DCOPReplyDelayed:
00394         {
00395             QDataStream ds( dataReceived, IO_ReadOnly );
00396             QCString calledApp, app;
00397             Q_INT32 id;
00398 
00399             ds >> calledApp >> app >> id;
00400             if (replyStruct && (id == replyStruct->transactionId) && (calledApp == replyStruct->calledApp))
00401             {
00402                 *replyWaitRet = True;
00403             }
00404 
00405             for(DCOPClient::ReplyStruct *rs = d->pendingReplies.first(); rs; 
00406                 rs = d->pendingReplies.next())
00407             {
00408                 if ((rs->transactionId == id) && (rs->calledApp == calledApp))
00409                 {
00410                     d->pendingReplies.remove();
00411                     QByteArray* b = rs->replyData;
00412                     QCString* t =  rs->replyType;
00413                     ds >> *t >> *b;
00414 
00415                     rs->status = DCOPClient::ReplyStruct::Ok;
00416                     rs->transactionId = 0;
00417                     if (!rs->replySlot.isEmpty())
00418                     {
00419                         d->parent->handleAsyncReply(rs);
00420                     }
00421                     return;
00422                 }
00423             }
00424         }
00425         qWarning("Very strange! got a DCOPReplyDelayed opcode, but we were not waiting for a reply!");
00426         return;
00427     case DCOPCall:
00428     case DCOPFind:
00429     case DCOPSend:
00430         DCOPProcessInternal( d, opcode, key, dataReceived, true );
00431     }
00432 }
00433 
00434 void DCOPClient::processPostedMessagesInternal()
00435 {
00436     if ( d->messages.isEmpty() )
00437         return;
00438     QPtrListIterator<DCOPClientMessage> it (d->messages );
00439     DCOPClientMessage* msg ;
00440     while ( ( msg = it.current() ) ) {
00441         ++it;
00442         if ( d->currentKey && msg->key != d->currentKey )
00443             continue;
00444         d->messages.removeRef( msg );
00445         d->opcode = msg->opcode;
00446         DCOPProcessInternal( d, msg->opcode, msg->key, msg->data, false );
00447         delete msg;
00448     }
00449     if ( !d->messages.isEmpty() )
00450         d->postMessageTimer.start( 100, true );
00451 }
00452 
00456 void DCOPProcessInternal( DCOPClientPrivate *d, int opcode, CARD32 key, const QByteArray& dataReceived, bool canPost  )
00457 {
00458     if (!d->accept_calls && (opcode == DCOPSend))
00459         return;
00460 
00461     IceConn iceConn = d->iceConn;
00462     DCOPMsg *pMsg = 0;
00463     DCOPClient *c = d->parent;
00464     QDataStream ds( dataReceived, IO_ReadOnly );
00465 
00466     QCString fromApp;
00467     ds >> fromApp;
00468     if (fromApp.isEmpty())
00469         return; // Reserved for local calls
00470 
00471     if (!d->accept_calls)
00472     {
00473         QByteArray reply;
00474         QDataStream replyStream( reply, IO_WriteOnly );
00475         // Call rejected.
00476         replyStream << d->appId << fromApp;
00477         IceGetHeader( iceConn, d->majorOpcode, DCOPReplyFailed,
00478                       sizeof(DCOPMsg), DCOPMsg, pMsg );
00479         int datalen = reply.size();
00480         pMsg->key = key;
00481         pMsg->length += datalen;
00482         IceSendData( iceConn, datalen, const_cast<char *>(reply.data()));
00483         return;
00484     }
00485 
00486     QCString app, objId, fun;
00487     QByteArray data;
00488     ds >> app >> objId >> fun >> data;
00489     d->senderId = fromApp;
00490     d->objId = objId;
00491     d->function = fun;
00492 
00493 // qWarning("DCOP: %s got call: %s:%s:%s key = %d currentKey = %d", d->appId.data(), app.data(), objId.data(), fun.data(), key, d->currentKey);
00494 
00495     if ( canPost && d->currentKey && key != d->currentKey ) {
00496         DCOPClientMessage* msg = new DCOPClientMessage;
00497         msg->opcode = opcode;
00498         msg->key = key;
00499         msg->data = dataReceived;
00500         d->messages.append( msg );
00501         d->postMessageTimer.start( 0, true );
00502         return;
00503     }
00504 
00505     d->objId = objId;
00506     d->function = fun;
00507 
00508     QCString replyType;
00509     QByteArray replyData;
00510     bool b;
00511     CARD32 oldCurrentKey = d->currentKey;
00512     if ( opcode != DCOPSend ) // DCOPSend doesn't change the current key
00513         d->currentKey = key;
00514 
00515     if ( opcode == DCOPFind )
00516         b = c->find(app, objId, fun, data, replyType, replyData );
00517     else
00518         b = c->receive( app, objId, fun, data, replyType, replyData );
00519     // set notifier back to previous state
00520 
00521     if ( opcode == DCOPSend )
00522         return;
00523 
00524     if ((d->currentKey == key) || (oldCurrentKey != 2))
00525         d->currentKey = oldCurrentKey;
00526 
00527     QByteArray reply;
00528     QDataStream replyStream( reply, IO_WriteOnly );
00529 
00530     Q_INT32 id = c->transactionId();
00531     if (id) {
00532         // Call delayed. Send back the transaction ID.
00533         replyStream << d->appId << fromApp << id;
00534 
00535         IceGetHeader( iceConn, d->majorOpcode, DCOPReplyWait,
00536                       sizeof(DCOPMsg), DCOPMsg, pMsg );
00537         pMsg->key = key;
00538         pMsg->length += reply.size();
00539         IceSendData( iceConn, reply.size(), const_cast<char *>(reply.data()));
00540         return;
00541     }
00542 
00543     if ( !b )        {
00544         // Call failed. No data send back.
00545 
00546         replyStream << d->appId << fromApp;
00547         IceGetHeader( iceConn, d->majorOpcode, DCOPReplyFailed,
00548                       sizeof(DCOPMsg), DCOPMsg, pMsg );
00549         int datalen = reply.size();
00550         pMsg->key = key;
00551         pMsg->length += datalen;
00552         IceSendData( iceConn, datalen, const_cast<char *>(reply.data()));
00553         return;
00554     }
00555 
00556     // Call successful. Send back replyType and replyData.
00557     replyStream << d->appId << fromApp << replyType << replyData.size();
00558 
00559 
00560     // we are calling, so we need to set up reply data
00561     IceGetHeader( iceConn, d->majorOpcode, DCOPReply,
00562                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00563     int datalen = reply.size() + replyData.size();
00564     pMsg->key = key;
00565     pMsg->length += datalen;
00566     // use IceSendData not IceWriteData to avoid a copy.  Output buffer
00567     // shouldn't need to be flushed.
00568     IceSendData( iceConn, reply.size(), const_cast<char *>(reply.data()));
00569     IceSendData( iceConn, replyData.size(), const_cast<char *>(replyData.data()));
00570 }
00571 
00572 
00573 
00574 static IcePoVersionRec DCOPClientVersions[] = {
00575     { DCOPVersionMajor, DCOPVersionMinor,  DCOPProcessMessage }
00576 };
00577 
00578 
00579 static DCOPClient* dcop_main_client = 0;
00580 
00581 DCOPClient* DCOPClient::mainClient()
00582 {
00583     return dcop_main_client;
00584 }
00585 
00586 void DCOPClient::setMainClient( DCOPClient* client )
00587 {
00588     dcop_main_client = client;
00589 }
00590 
00591 
00592 DCOPClient::DCOPClient()
00593 {
00594     d = new DCOPClientPrivate;
00595     d->parent = this;
00596     d->iceConn = 0L;
00597     d->key = 0;
00598     d->currentKey = 0;
00599     d->majorOpcode = 0;
00600     d->appId = 0;
00601     d->notifier = 0L;
00602     d->non_blocking_call_lock = false;
00603     d->registered = false;
00604     d->foreign_server = true;
00605     d->accept_calls = true;
00606     d->accept_calls_override = false;
00607     d->qt_bridge_enabled = true;
00608     d->transactionList = 0L;
00609     d->transactionId = 0;
00610     QObject::connect( &d->postMessageTimer, SIGNAL( timeout() ), this, SLOT( processPostedMessagesInternal() ) );
00611     QObject::connect( &d->eventLoopTimer, SIGNAL( timeout() ), this, SLOT( eventLoopTimeout() ) );
00612 
00613     if ( !mainClient() )
00614         setMainClient( this );
00615 }
00616 
00617 DCOPClient::~DCOPClient()
00618 {
00619 #ifdef DCOPCLIENT_DEBUG
00620     qWarning("d->messages.count() = %d", d->messages.count());
00621     QPtrListIterator<DCOPClientMessage> it (d->messages );
00622     DCOPClientMessage* msg ;
00623     while ( ( msg = it.current() ) ) {
00624         ++it;
00625         d->messages.removeRef( msg );
00626         qWarning("DROPPING UNHANDLED DCOP MESSAGE:");
00627         qWarning("         opcode = %d key = %d", msg->opcode, msg->key);
00628         QDataStream ds( msg->data, IO_ReadOnly );
00629 
00630         QCString fromApp, app, objId, fun;
00631         ds >> fromApp >> app >> objId >> fun;
00632         qWarning("         from = %s", fromApp.data()); 
00633         qWarning("         to = %s / %s / %s", app.data(), objId.data(), fun.data());
00634         delete msg;
00635     }
00636 #endif
00637     if (d->iceConn)
00638         if (IceConnectionStatus(d->iceConn) == IceConnectAccepted)
00639             detach();
00640 
00641     if (d->registered)
00642         unregisterLocalClient( d->appId );
00643 
00644     delete d->notifier;
00645     delete d->transactionList;
00646     d->messages.setAutoDelete(true);
00647     delete d;
00648 
00649     if ( mainClient() == this )
00650         setMainClient( 0 );
00651 }
00652 
00653 void DCOPClient::setServerAddress(const QCString &addr)
00654 {
00655     QCString env = "DCOPSERVER=" + addr;
00656     putenv(strdup(env.data()));
00657     delete [] DCOPClientPrivate::serverAddr;
00658     DCOPClientPrivate::serverAddr = qstrdup( addr.data() );
00659 }
00660 
00661 bool DCOPClient::attach()
00662 {
00663     if (!attachInternal( true ))
00664        if (!attachInternal( true ))
00665           return false; // Try two times!
00666     return true;
00667 }
00668 
00669 void DCOPClient::bindToApp()
00670 {
00671     // check if we have a qApp instantiated.  If we do,
00672     // we can create a QSocketNotifier and use it for receiving data.
00673     if (qApp) {
00674         if ( d->notifier )
00675             delete d->notifier;
00676         d->notifier = new QSocketNotifier(socket(),
00677                                           QSocketNotifier::Read, 0, 0);
00678         QObject::connect(d->notifier, SIGNAL(activated(int)),
00679                 SLOT(processSocketData(int)));
00680     }
00681 }
00682 
00683 void DCOPClient::suspend()
00684 {
00685 #ifdef Q_WS_WIN //TODO: remove (win32 ports sometimes do not create notifiers)
00686     if (!d->notifier)
00687         return;
00688 #endif
00689     assert(d->notifier); // Suspending makes no sense if we didn't had a qApp yet
00690     d->notifier->setEnabled(false);
00691 }
00692 
00693 void DCOPClient::resume()
00694 {
00695 #ifdef Q_WS_WIN //TODO: remove
00696     if (!d->notifier)
00697         return;
00698 #endif
00699     assert(d->notifier); // Should never happen
00700     d->notifier->setEnabled(true);
00701 }
00702 
00703 bool DCOPClient::isSuspended() const
00704 {
00705 #if defined(Q_WS_WIN) || defined(Q_WS_MAC) //TODO: REMOVE
00706     if (!d->notifier)
00707         return false;
00708 #endif
00709     return !d->notifier->isEnabled();
00710 }
00711 
00712 #ifdef SO_PEERCRED
00713 // Check whether the remote end is owned by the same user.
00714 static bool peerIsUs(int sockfd)
00715 {
00716     struct ucred cred;
00717     socklen_t siz = sizeof(cred);
00718     if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) != 0)
00719         return false;
00720     return (cred.uid == getuid());
00721 }
00722 #else
00723 // Check whether the socket is owned by the same user.
00724 static bool isServerSocketOwnedByUser(const char*server)
00725 {
00726 #ifdef Q_OS_WIN
00727     if (strncmp(server, "tcp/", 4) != 0)
00728         return false; // Not a local socket -> foreign.
00729     else
00730         return true;
00731 #else
00732     if (strncmp(server, "local/", 6) != 0)
00733         return false; // Not a local socket -> foreign.
00734     const char *path = strchr(server, KPATH_SEPARATOR);
00735     if (!path)
00736         return false;
00737     path++;
00738 
00739     struct stat stat_buf;
00740     if (stat(path, &stat_buf) != 0)
00741         return false;
00742 
00743     return (stat_buf.st_uid == getuid());
00744 #endif
00745 }
00746 #endif
00747 
00748 
00749 bool DCOPClient::attachInternal( bool registerAsAnonymous )
00750 {
00751     char errBuf[1024];
00752 
00753     if ( isAttached() )
00754         detach();
00755 
00756     if ((d->majorOpcode = IceRegisterForProtocolSetup(const_cast<char *>("DCOP"),
00757                                                       const_cast<char *>(DCOPVendorString),
00758                                                       const_cast<char *>(DCOPReleaseString),
00759                                                       1, DCOPClientVersions,
00760                                                       DCOPAuthCount,
00761                                                       const_cast<char **>(DCOPAuthNames),
00762                                                       DCOPClientAuthProcs, 0L)) < 0) {
00763         emit attachFailed(QString::fromLatin1( "Communications could not be established." ));
00764         return false;
00765     }
00766 
00767     bool bClearServerAddr = false;
00768     // first, check if serverAddr was ever set.
00769     if (!d->serverAddr) {
00770         // here, we obtain the list of possible DCOP connections,
00771         // and attach to them.
00772         QCString dcopSrv;
00773         dcopSrv = ::getenv("DCOPSERVER");
00774         if (dcopSrv.isEmpty()) {
00775             QCString fName = dcopServerFile();
00776             QFile f(QFile::decodeName(fName));
00777             if (!f.open(IO_ReadOnly)) {
00778                 emit attachFailed(QString::fromLatin1( "Could not read network connection list.\n" )+QFile::decodeName(fName));
00779                 return false;
00780             }
00781             int size = QMIN( 1024, f.size() ); // protection against a huge file
00782             QCString contents( size+1 );
00783             if ( f.readBlock( contents.data(), size ) != size )
00784             {
00785                qDebug("Error reading from %s, didn't read the expected %d bytes", fName.data(), size);
00786                // Should we abort ?
00787             }
00788             contents[size] = '\0';
00789             int pos = contents.find('\n');
00790             if ( pos == -1 ) // Shouldn't happen
00791             {
00792                 qDebug("Only one line in dcopserver file !: %s", contents.data());
00793                 dcopSrv = contents;
00794             }
00795             else
00796             {
00797                 if(contents[pos - 1] == '\r')   // check for windows end of line
00798                     pos--;
00799                 dcopSrv = contents.left( pos );
00800 //#ifndef NDEBUG
00801 //                qDebug("dcopserver address: %s", dcopSrv.data());
00802 //#endif
00803             }
00804         }
00805         d->serverAddr = qstrdup( const_cast<char *>(dcopSrv.data()) );
00806         bClearServerAddr = true;
00807     }
00808 
00809     if ((d->iceConn = IceOpenConnection(const_cast<char*>(d->serverAddr),
00810                                         static_cast<IcePointer>(this), False, d->majorOpcode,
00811                                         sizeof(errBuf), errBuf)) == 0L) {
00812         qDebug("DCOPClient::attachInternal. Attach failed %s", errBuf);
00813         d->iceConn = 0;
00814         if (bClearServerAddr) {
00815            delete [] d->serverAddr;
00816            d->serverAddr = 0;
00817         }
00818         emit attachFailed(QString::fromLatin1( errBuf ));
00819         return false;
00820     }
00821 
00822     IceSetShutdownNegotiation(d->iceConn, False);
00823 
00824     int setupstat;
00825     char* vendor = 0;
00826     char* release = 0;
00827     setupstat = IceProtocolSetup(d->iceConn, d->majorOpcode,
00828                                  static_cast<IcePointer>(d),
00829                                  False, /* must authenticate */
00830                                  &(d->majorVersion), &(d->minorVersion),
00831                                  &(vendor), &(release), 1024, errBuf);
00832     if (vendor) free(vendor);
00833     if (release) free(release);
00834 
00835     if (setupstat == IceProtocolSetupFailure ||
00836         setupstat == IceProtocolSetupIOError) {
00837         IceCloseConnection(d->iceConn);
00838         d->iceConn = 0;
00839         if (bClearServerAddr) {
00840             delete [] d->serverAddr;
00841             d->serverAddr = 0;
00842         }
00843         emit attachFailed(QString::fromLatin1( errBuf ));
00844         return false;
00845     } else if (setupstat == IceProtocolAlreadyActive) {
00846         if (bClearServerAddr) {
00847             delete [] d->serverAddr;
00848             d->serverAddr = 0;
00849         }
00850         /* should not happen because 3rd arg to IceOpenConnection was 0. */
00851         emit attachFailed(QString::fromLatin1( "internal error in IceOpenConnection" ));
00852         return false;
00853     }
00854 
00855 
00856     if (IceConnectionStatus(d->iceConn) != IceConnectAccepted) {
00857         if (bClearServerAddr) {
00858             delete [] d->serverAddr;
00859             d->serverAddr = 0;
00860         }
00861         emit attachFailed(QString::fromLatin1( "DCOP server did not accept the connection." ));
00862         return false;
00863     }
00864 
00865 #ifdef SO_PEERCRED
00866     d->foreign_server = !peerIsUs(socket());
00867 #else
00868     d->foreign_server = !isServerSocketOwnedByUser(d->serverAddr);
00869 #endif
00870     if (!d->accept_calls_override)
00871         d->accept_calls = !d->foreign_server;
00872 
00873     bindToApp();
00874 
00875     if ( registerAsAnonymous )
00876         registerAs( "anonymous", true );
00877 
00878     return true;
00879 }
00880 
00881 
00882 bool DCOPClient::detach()
00883 {
00884     int status;
00885 
00886     if (d->iceConn) {
00887         IceProtocolShutdown(d->iceConn, d->majorOpcode);
00888         status = IceCloseConnection(d->iceConn);
00889         if (status != IceClosedNow)
00890             return false;
00891         else
00892             d->iceConn = 0L;
00893     }
00894 
00895     if (d->registered)
00896         unregisterLocalClient(d->appId);
00897 
00898     delete d->notifier;
00899     d->notifier = 0L;
00900     d->registered = false;
00901     d->foreign_server = true;
00902     return true;
00903 }
00904 
00905 bool DCOPClient::isAttached() const
00906 {
00907     if (!d->iceConn)
00908         return false;
00909 
00910     return (IceConnectionStatus(d->iceConn) == IceConnectAccepted);
00911 }
00912 
00913 bool DCOPClient::isAttachedToForeignServer() const
00914 {
00915     return isAttached() && d->foreign_server;
00916 }
00917 
00918 bool DCOPClient::acceptCalls() const
00919 {
00920     return isAttached() && d->accept_calls;
00921 }
00922 
00923 void DCOPClient::setAcceptCalls(bool b)
00924 {
00925     d->accept_calls = b;
00926     d->accept_calls_override = true;
00927 }
00928 
00929 bool DCOPClient::qtBridgeEnabled()
00930 {
00931     return d->qt_bridge_enabled;
00932 }
00933 
00934 void DCOPClient::setQtBridgeEnabled(bool b)
00935 {
00936     d->qt_bridge_enabled = b;
00937 }
00938 
00939 QCString DCOPClient::registerAs( const QCString &appId, bool addPID )
00940 {
00941     QCString result;
00942 
00943     QCString _appId = appId;
00944 
00945     if (addPID) {
00946         QCString pid;
00947         pid.sprintf("-%d", getpid());
00948         _appId = _appId + pid;
00949     }
00950 
00951     if( d->appId == _appId )
00952         return d->appId;
00953 
00954 #if 0 // no need to detach, dcopserver can handle renaming
00955     // Detach before reregistering.
00956     if ( isRegistered() ) {
00957         detach();
00958     }
00959 #endif
00960 
00961     if ( !isAttached() ) {
00962         if (!attachInternal( false ))
00963             if (!attachInternal( false ))
00964                 return result; // Try two times
00965     }
00966 
00967     // register the application identifier with the server
00968     QCString replyType;
00969     QByteArray data, replyData;
00970     QDataStream arg( data, IO_WriteOnly );
00971     arg << _appId;
00972     if ( call( "DCOPServer", "", "registerAs(QCString)", data, replyType, replyData ) ) {
00973         QDataStream reply( replyData, IO_ReadOnly );
00974         reply >> result;
00975     }
00976 
00977     d->appId = result;
00978     d->registered = !result.isNull();
00979 
00980     if (d->registered)
00981         registerLocalClient( d->appId, this );
00982 
00983     return result;
00984 }
00985 
00986 bool DCOPClient::isRegistered() const
00987 {
00988     return d->registered;
00989 }
00990 
00991 
00992 QCString DCOPClient::appId() const
00993 {
00994     return d->appId;
00995 }
00996 
00997 
00998 int DCOPClient::socket() const
00999 {
01000     if (d->iceConn)
01001         return IceConnectionNumber(d->iceConn);
01002     return 0;
01003 }
01004 
01005 static inline bool isIdentChar( char x )
01006 {                                                // Avoid bug in isalnum
01007     return x == '_' || (x >= '0' && x <= '9') ||
01008          (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z');
01009 }
01010 
01011 QCString DCOPClient::normalizeFunctionSignature( const QCString& fun ) {
01012     if ( fun.isEmpty() )                                // nothing to do
01013         return fun.copy();
01014     QCString result( fun.size() );
01015     char *from        = fun.data();
01016     char *to        = result.data();
01017     char *first = to;
01018     char last = 0;
01019     while ( true ) {
01020         while ( *from && isspace(*from) )
01021             from++;
01022         if ( last && isIdentChar( last ) && isIdentChar( *from ) )
01023             *to++ = 0x20;
01024         while ( *from && !isspace(*from) ) {
01025             last = *from++;
01026             *to++ = last;
01027         }
01028         if ( !*from )
01029             break;
01030     }
01031     if ( to > first && *(to-1) == 0x20 )
01032         to--;
01033     *to = '\0';
01034     result.resize( (int)((long)to - (long)result.data()) + 1 );
01035     return result;
01036 }
01037 
01038 
01039 QCString DCOPClient::senderId() const
01040 {
01041     return d->senderId;
01042 }
01043 
01044 
01045 bool DCOPClient::send(const QCString &remApp, const QCString &remObjId,
01046                       const QCString &remFun, const QByteArray &data)
01047 {
01048     if (remApp.isEmpty())
01049        return false;
01050     DCOPClient *localClient = findLocalClient( remApp );
01051 
01052     if ( localClient  ) {
01053         bool saveTransaction = d->transaction;
01054         Q_INT32 saveTransactionId = d->transactionId;
01055         QCString saveSenderId = d->senderId;
01056 
01057         d->senderId = 0; // Local call
01058         QCString replyType;
01059         QByteArray replyData;
01060         (void) localClient->receive(  remApp, remObjId, remFun, data, replyType, replyData );
01061 
01062         d->transaction = saveTransaction;
01063         d->transactionId = saveTransactionId;
01064         d->senderId = saveSenderId;
01065         // send() returns true if the data could be send to the DCOPServer,
01066         // regardles of receiving the data on the other application.
01067         // So we assume the data is successfully send to the (virtual) server
01068         // and return true in any case.
01069         return true;
01070     }
01071 
01072     if ( !isAttached() )
01073         return false;
01074 
01075 
01076     DCOPMsg *pMsg;
01077 
01078     QByteArray ba;
01079     QDataStream ds(ba, IO_WriteOnly);
01080     ds << d->appId << remApp << remObjId << normalizeFunctionSignature(remFun) << data.size();
01081 
01082     IceGetHeader(d->iceConn, d->majorOpcode, DCOPSend,
01083                  sizeof(DCOPMsg), DCOPMsg, pMsg);
01084 
01085     pMsg->key = 1; // DCOPSend always uses the magic key 1
01086     int datalen = ba.size() + data.size();
01087     pMsg->length += datalen;
01088 
01089     IceSendData( d->iceConn, ba.size(), const_cast<char *>(ba.data()) );
01090     IceSendData( d->iceConn, data.size(), const_cast<char *>(data.data()) );
01091 
01092     //IceFlush(d->iceConn);
01093 
01094     if (IceConnectionStatus(d->iceConn) == IceConnectAccepted)
01095         return true;
01096     return false;
01097 }
01098 
01099 bool DCOPClient::send(const QCString &remApp, const QCString &remObjId,
01100                       const QCString &remFun, const QString &data)
01101 {
01102     QByteArray ba;
01103     QDataStream ds(ba, IO_WriteOnly);
01104     ds << data;
01105     return send(remApp, remObjId, remFun, ba);
01106 }
01107 
01108 bool DCOPClient::findObject(const QCString &remApp, const QCString &remObj,
01109                             const QCString &remFun, const QByteArray &data,
01110                             QCString &foundApp, QCString &foundObj,
01111                             bool useEventLoop)
01112 {
01113     return findObject( remApp, remObj, remFun, data, foundApp, foundObj, useEventLoop, -1 );
01114 }
01115 
01116 bool DCOPClient::findObject(const QCString &remApp, const QCString &remObj,
01117                             const QCString &remFun, const QByteArray &data,
01118                             QCString &foundApp, QCString &foundObj,
01119                             bool useEventLoop, int timeout)
01120 {
01121     QCStringList appList;
01122     QCString app = remApp;
01123     if (app.isEmpty())
01124         app = "*";
01125 
01126     foundApp = 0;
01127     foundObj = 0;
01128 
01129     if (app[app.length()-1] == '*')
01130     {
01131         // Find all apps that match 'app'.
01132         // NOTE: It would be more efficient to do the filtering in
01133         // the dcopserver itself.
01134         int len = app.length()-1;
01135         QCStringList apps=registeredApplications();
01136         for( QCStringList::ConstIterator it = apps.begin();
01137             it != apps.end();
01138             ++it)
01139         {
01140             if ( strncmp( (*it).data(), app.data(), len) == 0)
01141                 appList.append(*it);
01142         }
01143     }
01144     else
01145     {
01146         appList.append(app);
01147     }
01148 
01149     // We do all the local clients in phase1 and the rest in phase2
01150     for(int phase=1; phase <= 2; phase++)
01151     {
01152       for( QCStringList::ConstIterator it = appList.begin();
01153            it != appList.end();
01154            ++it)
01155       {
01156         QCString remApp = *it;
01157         QCString replyType;
01158         QByteArray replyData;
01159         bool result = false;
01160         DCOPClient *localClient = findLocalClient( remApp );
01161 
01162         if ( (phase == 1) && localClient ) {
01163             // In phase 1 we do all local clients
01164             bool saveTransaction = d->transaction;
01165             Q_INT32 saveTransactionId = d->transactionId;
01166             QCString saveSenderId = d->senderId;
01167 
01168             d->senderId = 0; // Local call
01169             result = localClient->find(  remApp, remObj, remFun, data, replyType, replyData );
01170 
01171             Q_INT32 id = localClient->transactionId();
01172             if (id) {
01173                 // Call delayed. We have to wait till it has been processed.
01174                 do {
01175                     QApplication::eventLoop()->processEvents( QEventLoop::WaitForMore);
01176                 } while( !localClient->isLocalTransactionFinished(id, replyType, replyData));
01177                 result = true;
01178             }
01179             d->transaction = saveTransaction;
01180             d->transactionId = saveTransactionId;
01181             d->senderId = saveSenderId;
01182         }
01183         else if ((phase == 2) && !localClient)
01184         {
01185             // In phase 2 we do the other clients
01186             result = callInternal(remApp, remObj, remFun, data,
01187                      replyType, replyData, useEventLoop, timeout, DCOPFind);
01188         }
01189 
01190         if (result)
01191         {
01192             if (replyType == "DCOPRef")
01193             {
01194                 DCOPRef ref;
01195                 QDataStream reply( replyData, IO_ReadOnly );
01196                 reply >> ref;
01197 
01198                 if (ref.app() == remApp) // Consistency check
01199                 {
01200                     // replyType contains objId.
01201                     foundApp = ref.app();
01202                     foundObj = ref.object();
01203                     return true;
01204                 }
01205             }
01206         }
01207       }
01208     }
01209     return false;
01210 }
01211 
01212 bool DCOPClient::process(const QCString &, const QByteArray &,
01213                          QCString&, QByteArray &)
01214 {
01215     return false;
01216 }
01217 
01218 bool DCOPClient::isApplicationRegistered( const QCString& remApp)
01219 {
01220     QCString replyType;
01221     QByteArray data, replyData;
01222     QDataStream arg( data, IO_WriteOnly );
01223     arg << remApp;
01224     int result = false;
01225     if ( call( "DCOPServer", "", "isApplicationRegistered(QCString)", data, replyType, replyData ) ) {
01226         QDataStream reply( replyData, IO_ReadOnly );
01227         reply >> result;
01228     }
01229     return result;
01230 }
01231 
01232