dcop Library API Documentation

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/types.h>
00040 #include <sys/stat.h>
00041 #include <sys/file.h>
00042 #include <sys/socket.h>
00043 
00044 #include <ctype.h>
00045 #include <unistd.h>
00046 #include <stdlib.h>
00047 #include <assert.h>
00048 #include <string.h>
00049 
00050 #ifndef QT_CLEAN_NAMESPACE
00051 #define QT_CLEAN_NAMESPACE
00052 #endif
00053 #include <qguardedptr.h>
00054 #include <qtextstream.h>
00055 #include <qfile.h>
00056 #include <qapplication.h>
00057 #include <qsocketnotifier.h>
00058 #include <qregexp.h>
00059 
00060 #include <private/qucomextra_p.h>
00061 
00062 #include <dcopglobal.h>
00063 #include <dcopclient.h>
00064 #include <dcopobject.h>
00065 
00066 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00067 #include <X11/Xmd.h> // schroder
00068 #endif
00069 extern "C" {
00070 #include <KDE-ICE/ICElib.h>
00071 #include <KDE-ICE/ICEutil.h>
00072 #include <KDE-ICE/ICEmsg.h>
00073 #include <KDE-ICE/ICEproto.h>
00074 
00075 
00076 #include <sys/time.h>
00077 #include <sys/types.h>
00078 #include <unistd.h>
00079 }
00080 
00081 extern QMap<QCString, DCOPObject *> * kde_dcopObjMap; // defined in dcopobject.cpp
00082 
00083 /*********************************************
00084  * Keep track of local clients
00085  *********************************************/
00086 typedef QAsciiDict<DCOPClient> client_map_t;
00087 static client_map_t *DCOPClient_CliMap = 0;
00088 
00089 static
00090 client_map_t *cliMap()
00091 {
00092     if (!DCOPClient_CliMap)
00093         DCOPClient_CliMap = new client_map_t;
00094     return DCOPClient_CliMap;
00095 }
00096 
00097 DCOPClient *DCOPClient::findLocalClient( const QCString &_appId )
00098 {
00099     return cliMap()->find(_appId.data());
00100 }
00101 
00102 static
00103 void registerLocalClient( const QCString &_appId, DCOPClient *client )
00104 {
00105     cliMap()->replace(_appId.data(), client);
00106 }
00107 
00108 static
00109 void unregisterLocalClient( const QCString &_appId )
00110 {
00111     client_map_t *map = cliMap();
00112     map->remove(_appId.data());
00113 }
00115 
00116 template class QPtrList<DCOPObjectProxy>;
00117 template class QPtrList<DCOPClientTransaction>;
00118 template class QPtrList<_IceConn>;
00119 
00120 struct DCOPClientMessage
00121 {
00122     int opcode;
00123     CARD32 key;
00124     QByteArray data;
00125 };
00126 
00127 class DCOPClient::ReplyStruct
00128 {
00129 public:
00130     enum ReplyStatus { Pending, Ok, Failed };
00131     ReplyStruct() {
00132         status = Pending;
00133         replyType = 0;
00134         replyData = 0;
00135         replyId = -1;
00136         transactionId = -1;
00137         replyObject = 0;
00138     }
00139     ReplyStatus status;
00140     QCString* replyType;
00141     QByteArray* replyData;
00142     int replyId;
00143     Q_INT32 transactionId;
00144     QCString calledApp;
00145     QGuardedPtr<QObject> replyObject;
00146     QCString replySlot;
00147 };
00148 
00149 class DCOPClientPrivate
00150 {
00151 public:
00152     DCOPClient *parent;
00153     QCString appId;
00154     IceConn iceConn;
00155     int majorOpcode; // major opcode negotiated w/server and used to tag all comms.
00156 
00157     int majorVersion, minorVersion; // protocol versions negotiated w/server
00158 
00159     static const char* serverAddr; // location of server in ICE-friendly format.
00160     QSocketNotifier *notifier;
00161     bool non_blocking_call_lock;
00162     bool registered;
00163     bool foreign_server;
00164     bool accept_calls;
00165     bool accept_calls_override; // If true, user has specified policy.
00166     bool qt_bridge_enabled;
00167 
00168     QCString senderId;
00169     QCString objId;
00170     QCString function;
00171 
00172     QCString defaultObject;
00173     QPtrList<DCOPClientTransaction> *transactionList;
00174     bool transaction;
00175     Q_INT32 transactionId;
00176     int opcode;
00177 
00178     // Special key values:
00179     // 0 : Not specified
00180     // 1 : DCOPSend
00181     // 2 : Priority
00182     // >= 42: Normal
00183     CARD32 key;
00184     CARD32 currentKey; 
00185     CARD32 currentKeySaved;
00186 
00187     QTimer postMessageTimer;
00188     QPtrList<DCOPClientMessage> messages;
00189 
00190     QPtrList<DCOPClient::ReplyStruct> pendingReplies;
00191     QPtrList<DCOPClient::ReplyStruct> asyncReplyQueue;
00192 
00193     struct LocalTransactionResult 
00194     {
00195         QCString replyType;
00196         QByteArray replyData;
00197     };
00198 
00199     QIntDict<LocalTransactionResult> localTransActionList;
00200 };
00201 
00202 class DCOPClientTransaction
00203 {
00204 public:
00205     Q_INT32 id;
00206     CARD32 key;
00207     QCString senderId;
00208 };
00209 
00210 QCString DCOPClient::iceauthPath()
00211 {
00212     QCString path = ::getenv("PATH");
00213     if (path.isEmpty())
00214         path = "/bin:/usr/bin";
00215     path += ":/usr/bin/X11:/usr/X11/bin:/usr/X11R6/bin";
00216     QCString fPath = strtok(path.data(), ":\b");
00217     while (!fPath.isNull())
00218     {
00219         fPath += "/iceauth";
00220         if (access(fPath.data(), X_OK) == 0)
00221         {
00222             return fPath;
00223         }
00224    
00225         fPath = strtok(NULL, ":\b");
00226     }
00227     return 0;
00228 }
00229 
00230 static QCString dcopServerFile(const QCString &hostname, bool old)
00231 {
00232     QCString fName = ::getenv("DCOPAUTHORITY");
00233     if (!old && !fName.isEmpty())
00234         return fName;
00235     
00236     fName = ::getenv("HOME");
00237     if (fName.isEmpty())
00238     {
00239         fprintf(stderr, "Aborting. $HOME is not set.\n");
00240         exit(1);
00241     }
00242 #ifdef Q_WS_X11
00243     QCString disp = getenv("DISPLAY");
00244 #elif defined(Q_WS_QWS)
00245     QCString disp = getenv("QWS_DISPLAY");
00246 #else
00247     QCString disp;
00248 #endif
00249     if (disp.isEmpty())
00250         disp = "NODISPLAY";
00251 
00252     int i;
00253     if((i = disp.findRev('.')) > disp.findRev(':') && i >= 0)
00254         disp.truncate(i);
00255 
00256     if (!old)
00257     {
00258         while( (i = disp.find(':')) >= 0)
00259             disp[i] = '_';
00260     }
00261 
00262     fName += "/.DCOPserver_";
00263     if (hostname.isEmpty())
00264     {
00265         char hostName[256];
00266         hostName[0] = '\0';
00267         if (gethostname(hostName, sizeof(hostName)))
00268         {
00269             fName += "localhost";
00270         }
00271         else 
00272         {
00273             hostName[sizeof(hostName)-1] = '\0';
00274             fName += hostName;
00275         }
00276     }
00277     else
00278     {
00279         fName += hostname;
00280     }
00281     fName += "_"+disp;
00282     return fName;
00283 }
00284 
00285 
00286 // static
00287 QCString DCOPClient::dcopServerFile(const QCString &hostname)
00288 {
00289     return ::dcopServerFile(hostname, false);
00290 }
00291 
00292 
00293 // static
00294 QCString DCOPClient::dcopServerFileOld(const QCString &hostname)
00295 {
00296     return ::dcopServerFile(hostname, true);
00297 }
00298 
00299 
00300 const char* DCOPClientPrivate::serverAddr = 0;
00301 
00302 static void DCOPProcessInternal( DCOPClientPrivate *d, int opcode, CARD32 key, const QByteArray& dataReceived, bool canPost  );
00303 
00304 void DCOPClient::handleAsyncReply(ReplyStruct *replyStruct)
00305 {
00306     if (replyStruct->replyObject)
00307     {
00308         QObject::connect(this, SIGNAL(callBack(int, const QCString&, const QByteArray &)),
00309                replyStruct->replyObject, replyStruct->replySlot);
00310         emit callBack(replyStruct->replyId, *(replyStruct->replyType), *(replyStruct->replyData));
00311         QObject::disconnect(this, SIGNAL(callBack(int, const QCString&, const QByteArray &)),
00312                replyStruct->replyObject, replyStruct->replySlot);
00313     }
00314     delete replyStruct;
00315 }
00316 
00320 static void DCOPProcessMessage(IceConn iceConn, IcePointer clientObject,
00321                         int opcode, unsigned long length, Bool /*swap*/,
00322                         IceReplyWaitInfo *replyWait,
00323                         Bool *replyWaitRet)
00324 {
00325     DCOPMsg *pMsg = 0;
00326     DCOPClientPrivate *d = static_cast<DCOPClientPrivate *>(clientObject);
00327     DCOPClient::ReplyStruct *replyStruct = replyWait ? static_cast<DCOPClient::ReplyStruct*>(replyWait->reply) : 0;
00328 
00329     IceReadMessageHeader(iceConn, sizeof(DCOPMsg), DCOPMsg, pMsg);
00330     CARD32 key = pMsg->key;
00331     if ( d->key == 0 )
00332         d->key = key; // received a key from the server
00333 
00334     QByteArray dataReceived( length );
00335     IceReadData(iceConn, length, dataReceived.data() );
00336 
00337     d->opcode = opcode;
00338     switch (opcode ) {
00339 
00340     case DCOPReplyFailed:
00341         if ( replyStruct ) {
00342             replyStruct->status = DCOPClient::ReplyStruct::Failed;
00343             replyStruct->transactionId = 0;
00344             *replyWaitRet = True;
00345             return;
00346         } else {
00347             qWarning("Very strange! got a DCOPReplyFailed opcode, but we were not waiting for a reply!");
00348             return;
00349         }
00350     case DCOPReply:
00351         if ( replyStruct ) {
00352             QByteArray* b = replyStruct->replyData;
00353             QCString* t =  replyStruct->replyType;
00354             replyStruct->status = DCOPClient::ReplyStruct::Ok;
00355             replyStruct->transactionId = 0;
00356 
00357             QCString calledApp, app;
00358             QDataStream ds( dataReceived, IO_ReadOnly );
00359             ds >> calledApp >> app >> *t >> *b;
00360 
00361             *replyWaitRet = True;
00362             return;
00363         } else {
00364             qWarning("Very strange! got a DCOPReply opcode, but we were not waiting for a reply!");
00365             return;
00366         }
00367     case DCOPReplyWait:
00368         if ( replyStruct ) {
00369             QCString calledApp, app;
00370             Q_INT32 id;
00371             QDataStream ds( dataReceived, IO_ReadOnly );
00372             ds >> calledApp >> app >> id;
00373             replyStruct->transactionId = id;
00374             replyStruct->calledApp = calledApp;
00375             d->pendingReplies.append(replyStruct);
00376             *replyWaitRet = True;
00377             return;
00378         } else {
00379             qWarning("Very strange! got a DCOPReplyWait opcode, but we were not waiting for a reply!");
00380             return;
00381         }
00382     case DCOPReplyDelayed:
00383         {
00384             QDataStream ds( dataReceived, IO_ReadOnly );
00385             QCString calledApp, app;
00386             Q_INT32 id;
00387 
00388             ds >> calledApp >> app >> id;
00389             if (replyStruct && (id == replyStruct->transactionId) && (calledApp = replyStruct->calledApp))
00390             {
00391                 *replyWaitRet = True;
00392             }
00393 
00394             for(DCOPClient::ReplyStruct *rs = d->pendingReplies.first(); rs; 
00395                 rs = d->pendingReplies.next())
00396             {
00397                 if ((rs->transactionId == id) && (rs->calledApp == calledApp))
00398                 {
00399                     d->pendingReplies.remove();
00400                     QByteArray* b = rs->replyData;
00401                     QCString* t =  rs->replyType;
00402                     ds >> *t >> *b;
00403 
00404                     rs->status = DCOPClient::ReplyStruct::Ok;
00405                     rs->transactionId = 0;
00406                     if (!rs->replySlot.isEmpty())
00407                     {
00408                         d->parent->handleAsyncReply(rs);
00409                     }
00410                     return;
00411                 }
00412             }
00413         }
00414         qWarning("Very strange! got a DCOPReplyDelayed opcode, but we were not waiting for a reply!");
00415         return;
00416     case DCOPCall:
00417     case DCOPFind:
00418     case DCOPSend:
00419         DCOPProcessInternal( d, opcode, key, dataReceived, true );
00420     }
00421 }
00422 
00423 
00424 void DCOPClient::processPostedMessagesInternal()
00425 {
00426     if ( d->messages.isEmpty() )
00427         return;
00428     QPtrListIterator<DCOPClientMessage> it (d->messages );
00429     DCOPClientMessage* msg ;
00430     while ( ( msg = it.current() ) ) {
00431         ++it;
00432         if ( d->currentKey && msg->key != d->currentKey )
00433             continue;
00434         d->messages.removeRef( msg );
00435         d->opcode = msg->opcode;
00436         DCOPProcessInternal( d, msg->opcode, msg->key, msg->data, false );
00437         delete msg;
00438     }
00439     if ( !d->messages.isEmpty() )
00440         d->postMessageTimer.start( 100, true );
00441 }
00442 
00446 void DCOPProcessInternal( DCOPClientPrivate *d, int opcode, CARD32 key, const QByteArray& dataReceived, bool canPost  )
00447 {
00448     if (!d->accept_calls && (opcode == DCOPSend))
00449         return;
00450 
00451     IceConn iceConn = d->iceConn;
00452     DCOPMsg *pMsg = 0;
00453     DCOPClient *c = d->parent;
00454     QDataStream ds( dataReceived, IO_ReadOnly );
00455 
00456     QCString fromApp;
00457     ds >> fromApp;
00458     if (fromApp.isEmpty())
00459         return; // Reserved for local calls
00460 
00461     if (!d->accept_calls)
00462     {
00463         QByteArray reply;
00464         QDataStream replyStream( reply, IO_WriteOnly );
00465         // Call rejected.
00466         replyStream << d->appId << fromApp;
00467         IceGetHeader( iceConn, d->majorOpcode, DCOPReplyFailed,
00468                       sizeof(DCOPMsg), DCOPMsg, pMsg );
00469         int datalen = reply.size();
00470         pMsg->key = key;
00471         pMsg->length += datalen;
00472         IceSendData( iceConn, datalen, const_cast<char *>(reply.data()));
00473         return;
00474     }
00475 
00476     QCString app, objId, fun;
00477     QByteArray data;
00478     ds >> app >> objId >> fun >> data;
00479     d->senderId = fromApp;
00480     d->objId = objId;
00481     d->function = fun;
00482 
00483 // qWarning("DCOP: %s got call: %s:%s:%s key = %d currentKey = %d", d->appId.data(), app.data(), objId.data(), fun.data(), key, d->currentKey);
00484 
00485     if ( canPost && d->currentKey && key != d->currentKey ) {
00486         DCOPClientMessage* msg = new DCOPClientMessage;
00487         msg->opcode = opcode;
00488         msg->key = key;
00489         msg->data = dataReceived;
00490         d->messages.append( msg );
00491         d->postMessageTimer.start( 0, true );
00492         return;
00493     }
00494 
00495     d->objId = objId;
00496     d->function = fun;
00497 
00498     QCString replyType;
00499     QByteArray replyData;
00500     bool b;
00501     CARD32 oldCurrentKey = d->currentKey;
00502     if ( opcode != DCOPSend ) // DCOPSend doesn't change the current key
00503         d->currentKey = key;
00504 
00505     if ( opcode == DCOPFind )
00506         b = c->find(app, objId, fun, data, replyType, replyData );
00507     else
00508         b = c->receive( app, objId, fun, data, replyType, replyData );
00509     // set notifier back to previous state
00510 
00511     if ( opcode == DCOPSend )
00512         return;
00513 
00514     if ((d->currentKey == key) || (oldCurrentKey != 2))
00515         d->currentKey = oldCurrentKey;
00516 
00517     QByteArray reply;
00518     QDataStream replyStream( reply, IO_WriteOnly );
00519 
00520     Q_INT32 id = c->transactionId();
00521     if (id) {
00522         // Call delayed. Send back the transaction ID.
00523         replyStream << d->appId << fromApp << id;
00524 
00525         IceGetHeader( iceConn, d->majorOpcode, DCOPReplyWait,
00526                       sizeof(DCOPMsg), DCOPMsg, pMsg );
00527         pMsg->key = key;
00528         pMsg->length += reply.size();
00529         IceSendData( iceConn, reply.size(), const_cast<char *>(reply.data()));
00530         return;
00531     }
00532 
00533     if ( !b )        {
00534         // Call failed. No data send back.
00535 
00536         replyStream << d->appId << fromApp;
00537         IceGetHeader( iceConn, d->majorOpcode, DCOPReplyFailed,
00538                       sizeof(DCOPMsg), DCOPMsg, pMsg );
00539         int datalen = reply.size();
00540         pMsg->key = key;
00541         pMsg->length += datalen;
00542         IceSendData( iceConn, datalen, const_cast<char *>(reply.data()));
00543         return;
00544     }
00545 
00546     // Call successful. Send back replyType and replyData.
00547     replyStream << d->appId << fromApp << replyType << replyData.size();
00548 
00549 
00550     // we are calling, so we need to set up reply data
00551     IceGetHeader( iceConn, d->majorOpcode, DCOPReply,
00552                   sizeof(DCOPMsg), DCOPMsg, pMsg );
00553     int datalen = reply.size() + replyData.size();
00554     pMsg->key = key;
00555     pMsg->length += datalen;
00556     // use IceSendData not IceWriteData to avoid a copy.  Output buffer
00557     // shouldn't need to be flushed.
00558     IceSendData( iceConn, reply.size(), const_cast<char *>(reply.data()));
00559     IceSendData( iceConn, replyData.size(), const_cast<char *>(replyData.data()));
00560 }
00561 
00562 
00563 
00564 static IcePoVersionRec DCOPClientVersions[] = {
00565     { DCOPVersionMajor, DCOPVersionMinor,  DCOPProcessMessage }
00566 };
00567 
00568 
00569 static DCOPClient* dcop_main_client = 0;
00570 
00571 DCOPClient* DCOPClient::mainClient()
00572 {
00573     return dcop_main_client;
00574 }
00575 
00576 void DCOPClient::setMainClient( DCOPClient* client )
00577 {
00578     dcop_main_client = client;
00579 }
00580 
00581 
00582 DCOPClient::DCOPClient()
00583 {
00584     d = new DCOPClientPrivate;
00585     d->parent = this;
00586     d->iceConn = 0L;
00587     d->majorOpcode = 0;
00588     d->key = 0;
00589     d->currentKey = 0;
00590     d->appId = 0;
00591     d->notifier = 0L;
00592     d->non_blocking_call_lock = false;
00593     d->registered = false;
00594     d->foreign_server = true;
00595     d->accept_calls = true;
00596     d->accept_calls_override = false;
00597     d->qt_bridge_enabled = true;
00598     d->transactionList = 0L;
00599     d->transactionId = 0;
00600     QObject::connect( &d->postMessageTimer, SIGNAL( timeout() ), this, SLOT( processPostedMessagesInternal() ) );
00601 
00602     if ( !mainClient() )
00603         setMainClient( this );
00604 }
00605 
00606 DCOPClient::~DCOPClient()
00607 {
00608     if (d->iceConn)
00609         if (IceConnectionStatus(d->iceConn) == IceConnectAccepted)
00610             detach();
00611 
00612     if (d->registered)
00613         unregisterLocalClient( d->appId );
00614 
00615     delete d->notifier;
00616     delete d->transactionList;
00617     delete d;
00618 
00619     if ( mainClient() == this )
00620         setMainClient( 0 );
00621 }
00622 
00623 void DCOPClient::setServerAddress(const QCString &addr)
00624 {
00625     QCString env = "DCOPSERVER=" + addr;
00626     putenv(strdup(env.data()));
00627     delete [] DCOPClientPrivate::serverAddr;
00628     DCOPClientPrivate::serverAddr = qstrdup( addr.data() );
00629 }
00630 
00631 bool DCOPClient::attach()
00632 {
00633     if (!attachInternal( true ))
00634        if (!attachInternal( true ))
00635           return false; // Try two times!
00636     return true;
00637 }
00638 
00639 void DCOPClient::bindToApp()
00640 {
00641     // check if we have a qApp instantiated.  If we do,
00642     // we can create a QSocketNotifier and use it for receiving data.
00643     if (qApp) {
00644         if ( d->notifier )
00645             delete d->notifier;
00646         d->notifier = new QSocketNotifier(socket(),
00647                                           QSocketNotifier::Read, 0, 0);
00648         QObject::connect(d->notifier, SIGNAL(activated(int)),
00649                 SLOT(processSocketData(int)));
00650     }
00651 }
00652 
00653 void DCOPClient::suspend()
00654 {
00655     assert(d->notifier); // Suspending makes no sense if we didn't had a qApp yet
00656     d->notifier->setEnabled(false);
00657 }
00658 
00659 void DCOPClient::resume()
00660 {
00661     assert(d->notifier); // Should never happen
00662     d->notifier->setEnabled(true);
00663 }
00664 
00665 bool DCOPClient::isSuspended() const
00666 {
00667     return !d->notifier->isEnabled();
00668 }
00669 
00670 #ifdef SO_PEERCRED
00671 // Check whether the remote end is owned by the same user.
00672 static bool peerIsUs(int sockfd)
00673 {
00674     struct ucred cred;
00675     socklen_t siz = sizeof(cred);
00676     if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &siz) != 0)
00677         return false;
00678     return (cred.uid == getuid());
00679 }
00680 #else
00681 // Check whether the socket is owned by the same user.
00682 static bool isServerSocketOwnedByUser(const char*server)
00683 {
00684     if (strncmp(server, "local/", 6) != 0)
00685         return false; // Not a local socket -> foreign.
00686     const char *path = strchr(server, ':');
00687     if (!path)
00688         return false;
00689     path++;
00690 
00691     struct stat stat_buf;
00692     if (stat(path, &stat_buf) != 0)
00693         return false;
00694 
00695     return (stat_buf.st_uid == getuid());
00696 }
00697 #endif
00698 
00699 
00700 bool DCOPClient::attachInternal( bool registerAsAnonymous )
00701 {
00702     char errBuf[1024];
00703 
00704     if ( isAttached() )
00705         detach();
00706 
00707     extern int _kde_IceLastMajorOpcode; // from libICE
00708     if (_kde_IceLastMajorOpcode < 1 )
00709         IceRegisterForProtocolSetup(const_cast<char *>("DUMMY"),
00710                                     const_cast<char *>("DUMMY"),
00711                                     const_cast<char *>("DUMMY"),
00712                                     1, DCOPClientVersions,
00713                                     DCOPAuthCount, const_cast<char **>(DCOPAuthNames),
00714                                     DCOPClientAuthProcs, 0);
00715     if (_kde_IceLastMajorOpcode < 1 )
00716         qWarning("DCOPClient Error: incorrect major opcode!");
00717 
00718     if ((d->majorOpcode = IceRegisterForProtocolSetup(const_cast<char *>("DCOP"),
00719                                                       const_cast<char *>(DCOPVendorString),
00720                                                       const_cast<char *>(DCOPReleaseString),
00721                                                       1, DCOPClientVersions,
00722                                                       DCOPAuthCount,
00723                                                       const_cast<char **>(DCOPAuthNames),
00724                                                       DCOPClientAuthProcs, 0L)) < 0) {
00725         emit attachFailed(QString::fromLatin1( "Communications could not be established." ));
00726         return false;
00727     }
00728 
00729     bool bClearServerAddr = false;
00730     // first, check if serverAddr was ever set.
00731     if (!d->serverAddr) {
00732         // here, we obtain the list of possible DCOP connections,
00733         // and attach to them.
00734         QString dcopSrv;
00735         dcopSrv = ::getenv("DCOPSERVER");
00736         if (dcopSrv.isEmpty()) {
00737             QString fName = dcopServerFile();
00738             QFile f(fName);
00739             if (!f.open(IO_ReadOnly)) {
00740                 emit attachFailed(QString::fromLatin1( "Could not read network connection list.\n" )+fName);
00741                 return false;
00742             }
00743             int size = QMIN( 1024, f.size() ); // protection against a huge file
00744             QCString contents( size+1 );
00745             if ( f.readBlock( contents.data(), size ) != size )
00746             {
00747                qDebug("Error reading from %s, didn't read the expected %d bytes", fName.latin1(), size);
00748                // Should we abort ?
00749             }
00750             contents[size] = '\0';
00751             int pos = contents.find('\n');
00752             if ( pos == -1 ) // Shouldn't happen
00753             {
00754                 qDebug("Only one line in dcopserver file !: %s", contents.data());
00755                 dcopSrv = QString::fromLatin1(contents);
00756             }
00757             else
00758             {
00759                 dcopSrv = QString::fromLatin1(contents.left( pos ));
00760 //#ifndef NDEBUG
00761 //                qDebug("dcopserver address: %s", dcopSrv.latin1());
00762 //#endif
00763             }
00764         }
00765         d->serverAddr = qstrdup( const_cast<char *>(dcopSrv.latin1()) );
00766         bClearServerAddr = true;
00767     }
00768 
00769     if ((d->iceConn = IceOpenConnection(const_cast<char*>(d->serverAddr),
00770                                         static_cast<IcePointer>(this), False, d->majorOpcode,
00771                                         sizeof(errBuf), errBuf)) == 0L) {
00772         qDebug("DCOPClient::attachInternal. Attach failed %s", errBuf ? errBuf : "");
00773         d->iceConn = 0;
00774         if (bClearServerAddr) {
00775            delete [] d->serverAddr;
00776            d->serverAddr = 0;
00777         }
00778         emit attachFailed(QString::fromLatin1( errBuf ));
00779         return false;
00780     }
00781 
00782     IceSetShutdownNegotiation(d->iceConn, False);
00783 
00784     int setupstat;
00785     char* vendor = 0;
00786     char* release = 0;
00787     setupstat = IceProtocolSetup(d->iceConn, d->majorOpcode,
00788                                  static_cast<IcePointer>(d),
00789                                  False, /* must authenticate */
00790                                  &(d->majorVersion), &(d->minorVersion),
00791                                  &(vendor), &(release), 1024, errBuf);
00792     if (vendor) free(vendor);
00793     if (release) free(release);
00794 
00795     if (setupstat == IceProtocolSetupFailure ||
00796         setupstat == IceProtocolSetupIOError) {
00797         IceCloseConnection(d->iceConn);
00798         d->iceConn = 0;
00799         if (bClearServerAddr) {
00800             delete [] d->serverAddr;
00801             d->serverAddr = 0;
00802         }
00803         emit attachFailed(QString::fromLatin1( errBuf ));
00804         return false;
00805     } else if (setupstat == IceProtocolAlreadyActive) {
00806         if (bClearServerAddr) {
00807             delete [] d->serverAddr;
00808             d->serverAddr = 0;
00809         }
00810         /* should not happen because 3rd arg to IceOpenConnection was 0. */
00811         emit attachFailed(QString::fromLatin1( "internal error in IceOpenConnection" ));
00812         return false;
00813     }
00814 
00815 
00816     if (IceConnectionStatus(d->iceConn) != IceConnectAccepted) {
00817         if (bClearServerAddr) {
00818             delete [] d->serverAddr;
00819             d->serverAddr = 0;
00820         }
00821         emit attachFailed(QString::fromLatin1( "DCOP server did not accept the connection." ));
00822         return false;
00823     }
00824 
00825 #ifdef SO_PEERCRED
00826     d->foreign_server = !peerIsUs(socket());
00827 #else
00828     d->foreign_server = !isServerSocketOwnedByUser(d->serverAddr);
00829 #endif
00830     if (!d->accept_calls_override)
00831         d->accept_calls = !d->foreign_server;
00832 
00833     bindToApp();
00834 
00835     if ( registerAsAnonymous )
00836         registerAs( "anonymous", true );
00837 
00838     return true;
00839 }
00840 
00841 
00842 bool DCOPClient::detach()
00843 {
00844     int status;
00845 
00846     if (d->iceConn) {
00847         IceProtocolShutdown(d->iceConn, d->majorOpcode);
00848         status = IceCloseConnection(d->iceConn);
00849         if (status != IceClosedNow)
00850             return false;
00851         else
00852             d->iceConn = 0L;
00853     }
00854 
00855     if (d->registered)
00856         unregisterLocalClient(d->appId);
00857 
00858     delete d->notifier;
00859     d->notifier = 0L;
00860     d->registered = false;
00861     d->foreign_server = true;
00862     return true;
00863 }
00864 
00865 bool DCOPClient::isAttached() const
00866 {
00867     if (!d->iceConn)
00868         return false;
00869 
00870     return (IceConnectionStatus(d->iceConn) == IceConnectAccepted);
00871 }
00872 
00873 bool DCOPClient::isAttachedToForeignServer() const
00874 {
00875     return isAttached() && d->foreign_server;
00876 }
00877 
00878 bool DCOPClient::acceptCalls() const
00879 {
00880     return isAttached() && d->accept_calls;
00881 }
00882 
00883 void DCOPClient::setAcceptCalls(bool b)
00884 {
00885     d->accept_calls = b;
00886     d->accept_calls_override = true;
00887 }
00888 
00889 bool DCOPClient::qtBridgeEnabled()
00890 {
00891     return d->qt_bridge_enabled;
00892 }
00893 
00894 void DCOPClient::setQtBridgeEnabled(bool b)
00895 {
00896     d->qt_bridge_enabled = b;
00897 }
00898 
00899 QCString DCOPClient::registerAs( const QCString &appId, bool addPID )
00900 {
00901     QCString result;
00902 
00903     QCString _appId = appId;
00904 
00905     if (addPID) {
00906         QCString pid;
00907         pid.sprintf("-%d", getpid());
00908         _appId = _appId + pid;
00909     }
00910 
00911     if( d->appId == _appId )
00912         return d->appId;
00913 
00914 #if 0 // no need to detach, dcopserver can handle renaming
00915     // Detach before reregistering.
00916     if ( isRegistered() ) {
00917         detach();
00918     }
00919 #endif
00920 
00921     if ( !isAttached() ) {
00922         if (!attachInternal( false ))
00923             if (!attachInternal( false ))
00924                 return result; // Try two times
00925     }
00926 
00927     // register the application identifier with the server
00928     QCString replyType;
00929     QByteArray data, replyData;
00930     QDataStream arg( data, IO_WriteOnly );
00931     arg << _appId;
00932     if ( call( "DCOPServer", "", "registerAs(QCString)", data, replyType, replyData ) ) {
00933         QDataStream reply( replyData, IO_ReadOnly );
00934         reply >> result;
00935     }
00936 
00937     d->appId = result;
00938     d->registered = !result.isNull();
00939 
00940     if (d->registered)
00941         registerLocalClient( d->appId, this );
00942 
00943     return result;
00944 }
00945 
00946 bool DCOPClient::isRegistered() const
00947 {
00948     return d->registered;
00949 }
00950 
00951 
00952 QCString DCOPClient::appId() const
00953 {
00954     return d->appId;
00955 }
00956 
00957 
00958 int DCOPClient::socket() const
00959 {
00960     if (d->iceConn)
00961         return IceConnectionNumber(d->iceConn);
00962     else
00963         return 0;
00964 }
00965 
00966 static inline bool isIdentChar( char x )
00967 {                                                // Avoid bug in isalnum
00968     return x == '_' || (x >= '0' && x <= '9') ||
00969          (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z');
00970 }
00971 
00972 QCString DCOPClient::normalizeFunctionSignature( const QCString& fun ) {
00973     if ( fun.isEmpty() )                                // nothing to do
00974         return fun.copy();
00975     QCString result( fun.size() );
00976     char *from        = fun.data();
00977     char *to        = result.data();
00978     char *first = to;
00979     char last = 0;
00980     while ( true ) {
00981         while ( *from && isspace(*from) )
00982             from++;
00983         if ( last && isIdentChar( last ) && isIdentChar( *from ) )
00984             *to++ = 0x20;
00985         while ( *from && !isspace(*from) ) {
00986             last = *from++;
00987             *to++ = last;
00988         }
00989         if ( !*from )
00990             break;
00991     }
00992     if ( to > first && *(to-1) == 0x20 )
00993         to--;
00994     *to = '\0';
00995     result.resize( (int)((long)to - (long)result.data()) + 1 );
00996     return result;
00997 }
00998 
00999 
01000 QCString DCOPClient::senderId() const
01001 {
01002     return d->senderId;
01003 }
01004 
01005 
01006 bool DCOPClient::send(const QCString &remApp, const QCString &remObjId,
01007                       const QCString &remFun, const QByteArray &data)
01008 {
01009     if (remApp.isEmpty())
01010        return false;
01011     DCOPClient *localClient = findLocalClient( remApp );
01012 
01013     if ( localClient  ) {
01014         bool saveTransaction = d->transaction;
01015         Q_INT32 saveTransactionId = d->transactionId;
01016         QCString saveSenderId = d->senderId;
01017 
01018         d->senderId = 0; // Local call
01019         QCString replyType;
01020         QByteArray replyData;
01021         (void) localClient->receive(  remApp, remObjId, remFun, data, replyType, replyData );
01022 
01023         d->transaction = saveTransaction;
01024         d->transactionId = saveTransactionId;
01025         d->senderId = saveSenderId;
01026         // send() returns true if the data could be send to the DCOPServer,
01027         // regardles of receiving the data on the other application.
01028         // So we assume the data is successfully send to the (virtual) server
01029         // and return true in any case.
01030         return true;
01031     }
01032 
01033     if ( !isAttached() )
01034         return false;
01035 
01036 
01037     DCOPMsg *pMsg;
01038 
01039     QByteArray ba;
01040     QDataStream ds(ba, IO_WriteOnly);
01041     ds << d->appId << remApp << remObjId << normalizeFunctionSignature(remFun) << data.size();
01042 
01043     IceGetHeader(d->iceConn, d->majorOpcode, DCOPSend,
01044                  sizeof(DCOPMsg), DCOPMsg, pMsg);
01045 
01046     pMsg->key = 1; // DCOPSend always uses the magic key 1
01047     int datalen = ba.size() + data.size();
01048     pMsg->length += datalen;
01049 
01050     IceSendData( d->iceConn, ba.size(), const_cast<char *>(ba.data()) );
01051     IceSendData( d->iceConn, data.size(), const_cast<char *>(data.data()) );
01052 
01053     //IceFlush(d->iceConn);
01054 
01055     if (IceConnectionStatus(d->iceConn) != IceConnectAccepted)
01056         return false;
01057     else
01058         return true;
01059 }
01060 
01061 bool DCOPClient::send(const QCString &remApp, const QCString &remObjId,
01062                       const QCString &remFun, const QString &data)
01063 {
01064     QByteArray ba;
01065     QDataStream ds(ba, IO_WriteOnly);
01066     ds << data;
01067     return send(remApp, remObjId, remFun, ba);
01068 }
01069 
01070 bool DCOPClient::findObject(const QCString &remApp, const QCString &remObj,
01071                             const QCString &remFun, const QByteArray &data,
01072                             QCString &foundApp, QCString &foundObj,
01073                             bool useEventLoop)
01074 {
01075     return findObject( remApp, remObj, remFun, data, foundApp, foundObj, useEventLoop, -1 );
01076 }
01077 
01078 bool DCOPClient::findObject(const QCString &remApp, const QCString &remObj,
01079                             const QCString &remFun, const QByteArray &data,
01080                             QCString &foundApp, QCString &foundObj,
01081                             bool useEventLoop, int timeout)
01082 {
01083     QCStringList appList;
01084     QCString app = remApp;
01085     if (app.isEmpty())
01086         app = "*";
01087 
01088     foundApp = 0;
01089     foundObj = 0;
01090 
01091     if (app[app.length()-1] == '*')
01092     {
01093         // Find all apps that match 'app'.
01094         // NOTE: It would be more efficient to do the filtering in
01095         // the dcopserver itself.
01096         int len = app.length()-1;
01097         QCStringList apps=registeredApplications();
01098         for( QCStringList::ConstIterator it = apps.begin();
01099             it != apps.end();
01100             ++it)
01101         {
01102             if ( strncmp( (*it).data(), app.data(), len) == 0)
01103                 appList.append(*it);
01104         }
01105     }
01106     else
01107     {
01108         appList.append(app);
01109     }
01110 
01111     // We do all the local clients in phase1 and the rest in phase2
01112     for(int phase=1; phase <= 2; phase++)
01113     {
01114       for( QCStringList::ConstIterator it = appList.begin();
01115            it != appList.end();
01116            ++it)
01117       {
01118         QCString remApp = *it;
01119         QCString replyType;
01120         QByteArray replyData;
01121         bool result;
01122         DCOPClient *localClient = findLocalClient( remApp );
01123 
01124         if ( (phase == 1) && localClient ) {
01125             // In phase 1 we do all local clients
01126             bool saveTransaction = d->transaction;
01127             Q_INT32 saveTransactionId = d->transactionId;
01128             QCString saveSenderId = d->senderId;
01129 
01130             d->senderId = 0; // Local call
01131             result = localClient->find(  remApp, remObj, remFun, data, replyType, replyData );
01132 
01133             Q_INT32 id = localClient->transactionId();
01134             if (id) {
01135                 // Call delayed. We have to wait till it has been processed.
01136                 do {
01137                     QApplication::eventLoop()->processEvents( QEventLoop::WaitForMore);
01138                 } while( !localClient->isLocalTransactionFinished(id, replyType, replyData));
01139                 result = true;
01140             }
01141             d->transaction = saveTransaction;
01142             d->transactionId = saveTransactionId;
01143             d->senderId = saveSenderId;
01144         }
01145         else if ((phase == 2) && !localClient)
01146         {
01147             // In phase 2 we do the other clients
01148             result = callInternal(remApp, remObj, remFun, data,
01149                      replyType, replyData, useEventLoop, timeout, DCOPFind);
01150         }
01151 
01152         if (result)
01153         {
01154             if (replyType == "DCOPRef")
01155             {
01156                 DCOPRef ref;
01157                 QDataStream reply( replyData, IO_ReadOnly );
01158                 reply >> ref;
01159 
01160                 if (ref.app() == remApp) // Consistency check
01161                 {
01162                     // replyType contains objId.
01163                     foundApp = ref.app();
01164                     foundObj = ref.object();
01165                     return true;
01166                 }
01167             }
01168         }
01169       }
01170     }
01171     return false;
01172 }
01173 
01174 bool DCOPClient::process(const QCString &, const QByteArray &,
01175                          QCString&, QByteArray &)
01176 {
01177     return false;
01178 }
01179 
01180 bool DCOPClient::isApplicationRegistered( const QCString& remApp)
01181 {
01182     QCString replyType;
01183     QByteArray data, replyData;
01184     QDataStream arg( data, IO_WriteOnly );
01185     arg << remApp;
01186     int result = false;
01187     if ( call( "DCOPServer", "", "isApplicationRegistered(QCString)", data, replyType, replyData ) ) {
01188         QDataStream reply( replyData, IO_ReadOnly );
01189         reply >> result;
01190     }
01191     return result;
01192 }
01193 
01194 QCStringList DCOPClient::registeredApplications()
01195 {
01196     QCString replyType;
01197     QByteArray data, replyData;
01198     QCStringList result;
01199     if ( call( "DCOPServer", "", "registeredApplications()", data, replyType, replyData ) ) {
01200         QDataStream reply( replyData, IO_ReadOnly );
01201         reply >> result;
01202     }
01203     return result;
01204 }
01205 
01206 QCStringList DCOPClient::remoteObjects( const QCString& remApp, bool *ok )
01207 {
01208     QCString replyType;
01209     QByteArray data, replyData;
01210     QCStringList result;
01211     if ( ok )
01212         *ok = false;
01213     if ( call( remApp, "DCOPClient", "objects()", data, replyType, replyData ) ) {
01214         QDataStream reply( replyData, IO_ReadOnly );
01215         reply >> result;
01216         if ( ok )
01217             *ok = true;
01218     }
01219     return result;
01220 }
01221 
01222 QCStringList DCOPClient::remoteInterfaces( const QCString& remApp, const QCString& remObj, bool *ok  )
01223 {
01224     QCString replyType;
01225     QByteArray data, replyData;
01226     QCStringList result;
01227     if ( ok )
01228         *ok = false;
01229     if ( call( remApp, remObj, "interfaces()", data, replyType, replyData ) && replyType == "QCStringList") {
01230         QDataStream reply( replyData, IO_ReadOnly );
01231         reply >> result;
01232         if ( ok )
01233             *ok = true;
01234     }
01235     return result;
01236 }
01237 
01238 QCStringList DCOPClient::remoteFunctions( const QCString& remApp, const QCString& remObj, bool *ok  )
01239 {
01240     QCString replyType;
01241     QByteArray data, replyData;
01242     QCStringList result;
01243     if ( ok )
01244         *ok = false;
01245     if ( call( remApp, remObj, "functions()", data, replyType, replyData ) && replyType == "QCStringList") {
01246         QDataStream reply( replyData, IO_ReadOnly );
01247         reply >> result;
01248         if ( ok )
01249             *ok = true;
01250     }
01251     return result;
01252 }
01253 
01254 void DCOPClient::setNotifications(bool enabled)
01255 {
01256     QByteArray data;
01257     QDataStream ds(data, IO_WriteOnly);
01258     ds << static_cast<Q_INT8>(enabled);
01259 
01260     QCString replyType;
01261     QByteArray reply;
01262     if (!call("DCOPServer", "", "setNotifications( bool )", data, replyType, reply))
01263         qWarning("I couldn't enable notifications at the dcopserver!");
01264 }
01265 
01266 void DCOPClient::setDaemonMode( bool daemonMode )
01267 {
01268     QByteArray data;
01269     QDataStream ds(data, IO_WriteOnly);
01270     ds << static_cast<Q_INT8>( daemonMode );
01271 
01272     QCString replyType;
01273     QByteArray reply;
01274     if (!call("DCOPServer", "", "setDaemonMode(bool)", data, replyType, reply))
01275         qWarning("I couldn't enable daemon mode at the dcopserver!");
01276 }
01277 
01278 
01279 
01280 /*
01281   DCOP <-> Qt bridge
01282 
01283   ********************************************************************************
01284  */
01285 static void fillQtObjects( QCStringList& l, QObject* o, QCString path )
01286 {
01287     if ( !path.isEmpty() )
01288         path += '/';
01289 
01290     int unnamed = 0;
01291     const QObjectList *list = o ? o->children() : QObject::objectTrees();
01292     if ( list ) {
01293         QObjectListIt it( *list );
01294         QObject *obj;
01295         while ( (obj=it.current()) ) {
01296             ++it;
01297              QCString n = obj->name();
01298              if ( n == "unnamed" || n.isEmpty() )
01299              {
01300                  n.sprintf("%p", (void *) obj);
01301                  n = QString("unnamed%1(%2, %3)").arg(++unnamed).arg(obj->className()).arg(n).latin1();
01302              }
01303              QCString fn = path + n;
01304              l.append( fn );
01305              if ( obj->children() )
01306                  fillQtObjects( l, obj, fn );
01307         }
01308     }
01309 }
01310 
01311 namespace
01312 {
01313 struct O
01314 {
01315     O(): o(0) {}
01316     O ( const QCString& str, QObject* obj ):s(str), o(obj){}
01317     QCString s;
01318     QObject* o;
01319 };
01320 } // namespace
01321 
01322 static void fillQtObjectsEx( QValueList<O>& l, QObject* o, QCString path )
01323 {
01324     if ( !path.isEmpty() )
01325         path += '/';
01326 
01327     int unnamed = 0;
01328     const QObjectList *list = o ? o->children() : QObject::objectTrees();
01329     if ( list ) {
01330         QObjectListIt it( *list );
01331         QObject *obj;
01332         while ( (obj=it.current()) ) {
01333             ++it;
01334             QCString n = obj->name();
01335             if ( n == "unnamed" || n.isEmpty() )
01336              {
01337                  n.sprintf("%p", (void *) obj);
01338                  n = QString("unnamed%1(%2, %3)").arg(++unnamed).arg(obj->className()).arg(n).latin1();
01339              }
01340             QCString fn = path + n;
01341             l.append( O( fn, obj ) );
01342             if ( obj->children() )
01343                 fillQtObjectsEx( l, obj, fn );
01344         }
01345     }
01346 }
01347 
01348 
01349 static QObject* findQtObject( QCString id )
01350 {
01351     QRegExp expr( id );
01352     QValueList<O> l;
01353     fillQtObjectsEx( l, 0, "qt" );
01354     // Prefer an exact match, but fall-back on the first that contains the substring
01355     QObject* firstContains = 0L;
01356     for ( QValueList<O>::ConstIterator it = l.begin(); it != l.end(); ++it ) {
01357         if ( (*it).s == id ) // exact match
01358             return (*it).o;
01359         if ( !firstContains && (*it).s.contains( expr ) ) {
01360             firstContains = (*it).o;
01361         }
01362     }
01363     return firstContains;
01364 }
01365 
01366 static QCStringList  findQtObjects( QCString id )
01367 {
01368     QRegExp expr( id );
01369     QValueList<O> l;
01370     fillQtObjectsEx( l, 0, "qt" );
01371     QCStringList result;
01372     for ( QValueList<O>::ConstIterator it = l.begin(); it != l.end(); ++it ) {
01373         if ( (*it).s.contains( expr ) )
01374             result << (*it).s;
01375     }
01376     return result;
01377 }
01378 
01379 static bool receiveQtObject( const QCString &objId, const QCString &fun, const QByteArray &data,
01380                             QCString& replyType, QByteArray &replyData)
01381 {
01382     if  ( objId == "qt" ) {
01383         if ( fun == "interfaces()" ) {
01384             replyType = "QCStringList";
01385             QDataStream reply( replyData, IO_WriteOnly );
01386             QCStringList l;
01387             l << "DCOPObject";
01388             l << "Qt";
01389             reply << l;
01390             return true;
01391         } else if ( fun == "functions()" ) {
01392             replyType = "QCStringList";
01393             QDataStream reply( replyData, IO_WriteOnly );
01394             QCStringList l;
01395             l << "QCStringList functions()";
01396             l << "QCStringList interfaces()";
01397             l << "QCStringList objects()";
01398             l << "QCStringList find(QCString)";
01399             reply << l;
01400             return true;
01401         } else if ( fun == "objects()" ) {
01402             replyType = "QCStringList";
01403             QDataStream reply( replyData, IO_WriteOnly );
01404             QCStringList l;
01405             fillQtObjects( l, 0, "qt" );
01406             reply << l;
01407             return true;
01408         } else if ( fun == "find(QCString)" ) {
01409             QDataStream ds( data, IO_ReadOnly );
01410             QCString id;
01411             ds >> id ;
01412             replyType = "QCStringList";
01413             QDataStream reply( replyData, IO_WriteOnly );
01414             reply << findQtObjects( id ) ;
01415             return true;
01416         }
01417     } else if ( objId.left(3) == "qt/" ) {
01418         QObject* o = findQtObject( objId );
01419         if ( !o )
01420             return false;
01421         if ( fun == "functions()" ) {
01422             replyType = "QCStringList";
01423             QDataStream reply( replyData, IO_WriteOnly );
01424             QCStringList l;
01425             l << "QCStringList functions()";
01426             l << "QCStringList interfaces()";
01427             l << "QCStringList properties()";
01428             l << "bool setProperty(QCString,QVariant)";
01429             l << "QVariant property(QCString)";
01430             QStrList lst = o->metaObject()->slotNames( true );
01431             int i = 0;
01432             for ( QPtrListIterator<char> it( lst ); it.current(); ++it ) {
01433                 if ( o->metaObject()->slot( i++, true )->access != QMetaData::Public )
01434                     continue;
01435                 QCString slot = it.current();
01436                 if ( slot.contains( "()" ) ) {
01437                     slot.prepend("void ");
01438                     l <<  slot;
01439                 }
01440             }
01441             reply << l;
01442             return true;
01443         } else if ( fun == "interfaces()" ) {
01444             replyType = "QCStringList";
01445             QDataStream reply( replyData, IO_WriteOnly );
01446             QCStringList l;
01447             QMetaObject *meta = o->metaObject();
01448             while ( meta ) {
01449                 l.prepend( meta->className() );
01450                 meta = meta->superClass();
01451             }
01452             reply << l;
01453             return true;
01454         } else if ( fun == "properties()" ) {
01455             replyType = "QCStringList";
01456             QDataStream reply( replyData, IO_WriteOnly );
01457             QCStringList l;
01458             QStrList lst = o->metaObject()->propertyNames( true );
01459             for ( QPtrListIterator<char> it( lst ); it.current(); ++it ) {
01460                 QMetaObject *mo = o->