diff options
Diffstat (limited to 'dcop/dcopc.c')
-rw-r--r-- | dcop/dcopc.c | 790 |
1 files changed, 790 insertions, 0 deletions
diff --git a/dcop/dcopc.c b/dcop/dcopc.c new file mode 100644 index 000000000..c62caf2ba --- /dev/null +++ b/dcop/dcopc.c @@ -0,0 +1,790 @@ +/* vi: set ts=2 sw=2 tw=78: + * + * C interface to DCOP + * + * (C) 2000 Rik Hemsley <rik@kde.org> + * (C) 2000 Simon Hausmann <hausmann@kde.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* sometimes __STDC__ is defined, but to 0. The hateful X headers + ask for '#if __STDC__', so they become confused. */ +#if defined(__STDC__) +#if !__STDC__ +#undef __STDC__ +#define __STDC__ 1 +#endif +#endif + +/* We really don't want to require X11 headers... */ + +#include "config.h" +#include <qglobal.h> +#if defined Q_WS_X11 && ! defined K_WS_QTONLY +#include <X11/Xlib.h> +#include <X11/X.h> +#include <X11/Xproto.h> +#include <X11/Xmd.h> + +#include <X11/ICE/ICE.h> +#include <X11/ICE/ICElib.h> +#include <X11/ICE/ICEutil.h> +#include <X11/ICE/ICEconn.h> +#include <X11/ICE/ICEmsg.h> +#include <X11/ICE/ICEproto.h> +#endif + +#include "dcopglobal.h" +#include "dcopc.h" + + +#define BUFFER_SIZE 1024 + +enum { + DCOP_REPLY_PENDING, + DCOP_REPLY_OK, + DCOP_REPLY_FAILED +}; + +struct dcop_reply_struct +{ + unsigned long status; + char ** replyType; + char ** replyData; + int * replyDataLength; + int replyId; +}; + + void +dcop_process_message( + IceConn iceConn, + IcePointer clientObject, + int opcode, + unsigned long length, + Bool swap, + IceReplyWaitInfo * replyWait, + Bool * replyWaitRet +); + +Bool dcop_attach_internal(Bool register_as_anonymous); +Bool dcop_ice_register(void); +Bool dcop_connect(void); +Bool dcop_protocol_setup(void); + +char * dcop_write_int (char * buf, int i); +char * dcop_read_int (char * buf, int * i); +char * dcop_write_string (char * buf, const char * text); +char * dcop_read_string (char * buf, char ** output); + +static char * dcop_requested_name = 0; +static char * dcop_app_name = 0; +static int dcop_major_opcode = 0; +static IceConn dcop_ice_conn = 0L; +static CARD32 dcop_key = 0; +static int dcop_reply_id = 0; + +static IcePoVersionRec DCOPVersions[] = { + { DCOPVersionMajor, DCOPVersionMinor, dcop_process_message } +}; + +/***************************************************************************/ + + char * +dcop_write_int(char * buf, int i) +{ + char * p = (char *)(&i); + + buf[3] = *p++; + buf[2] = *p++; + buf[1] = *p++; + buf[0] = *p; + + return buf + 4; +} + +/***************************************************************************/ + + char * +dcop_read_int(char * buf, int * i) +{ + char *p = (char *)i; + + *p++ = buf[3]; + *p++ = buf[2]; + *p++ = buf[1]; + *p = buf[0]; + + return buf + 4; +} + +/***************************************************************************/ + + char * +dcop_write_string(char * buf, const char * text) +{ + char * pos = buf; + int l = strlen( text ) + 1; /* we need the \0! (Simon) */ + pos = dcop_write_int(buf, l); + memcpy(pos, text, l); + return pos + l; +} + +/***************************************************************************/ + + char * +dcop_read_string(char * buf, char ** output) +{ + int length; + char * pos = dcop_read_int(buf, &length); + fprintf(stderr, "dcop_read_string: length == %d\n", length); + + *output = (char *)malloc(length); + memcpy(*output, pos, length); + return pos + length; +} + +/***************************************************************************/ + + Bool +dcop_register_callback(const char * object_id, dcop_callback_t callback) +{ + /* STUB */ + + /* Avoid unused param warnings */ + object_id = object_id; + callback = callback; + return False; + + /* + * TODO: + * Map the given object id to the given callback internally, so that when we + * receive a message, we know where to send it. Or... should we just allow + * one callback only, and pass that all calls ? Depends whether I can be + * bothered to figure out how to do a 'map' in C. + */ +} + +/***************************************************************************/ + + void +dcop_process_message( + IceConn iceConn, + IcePointer clientObject, + int opcode, + unsigned long length, + Bool swap, + IceReplyWaitInfo * replyWait, + Bool * replyWaitRet +) +{ + struct DCOPMsg * pMsg = 0L; + Status status = False; + + char * buf = 0L; + char * senderId = 0L; + char * app = 0L; + char * objId = 0L; + char * fun = 0L; + char * pos = 0L; + char * replyType = 0L; + + int dataLength = 0; + + unsigned long replyVal = 0L; + + /* Avoid unused param warnings */ + clientObject = clientObject; + swap = swap; + + if (0 == replyWait) { + fprintf(stderr, "dcop_process_message(): replyWait is 0\n"); + } + + if (iceConn != dcop_ice_conn) { + fprintf(stderr, "dcop_process_message(): ICE connection does not match\n"); + return; + } + + IceReadMessageHeader( + dcop_ice_conn, + sizeof(struct DCOPMsg), + struct DCOPMsg, + pMsg + ); + + + switch (opcode) { + + case DCOPReply: + fprintf(stderr, "dcop_process_message(): DCOPReply received\n"); + + fprintf(stderr, "dcop_process_message(): length == %ld\n", length); + buf = (char *)malloc(length); + status = IceReadData(dcop_ice_conn, length, buf); + if (False == status) { + fprintf(stderr, "dcop_process_message(): IceReadData failed\n"); + } + + fprintf(stderr, "dcop_process_message(): Reading data\n"); + pos = buf; + pos = dcop_read_string(pos, &replyType); + fprintf(stderr, "dcop_process_message(): replyType : `%s'\n", replyType); + + /* TODO: Run user-provided callback. */ + + free(replyType); + + replyVal = DCOP_REPLY_OK; + break; + + case DCOPReplyFailed: + fprintf(stderr, "dcop_process_message(): DCOPReplyFailed received\n"); + break; + + case DCOPReplyWait: + fprintf(stderr, "dcop_process_message(): DCOPReplyWait received\n"); + break; + + case DCOPReplyDelayed: + fprintf(stderr, "dcop_process_message(): DCOPReplyDelayed received\n"); + break; + + case DCOPFind: + fprintf(stderr, "dcop_process_message(): DCOPFind received\n"); + break; + + case DCOPSend: + + fprintf(stderr, "dcop_process_message(): DCOPSend received\n"); + + buf = (char *)malloc(length); + IceReadData(dcop_ice_conn, length, buf); + + pos = buf; + pos = dcop_read_string(pos, &senderId); + pos = dcop_read_string(pos, &app); + pos = dcop_read_string(pos, &objId); + pos = dcop_read_string(pos, &fun); + pos = dcop_read_int(pos, &dataLength); + + /* TODO: Run user-provided callback. */ + + free(senderId); + free(app); + free(objId); + free(fun); + free(buf); + + replyVal = DCOP_REPLY_OK; + break; + + case DCOPCall: + fprintf(stderr, "dcop_process_message(): DCOPCall not yet implemented\n"); + break; + + default: + fprintf(stderr, "dcop_process_message(): Invalid opcode %d\n", opcode); + break; + } + + if (0 != replyWait) + ((struct dcop_reply_struct *)replyWait->reply)->status = replyVal; + + fprintf(stderr, "dcop_process_message(): Setting replyWaitRet = True\n"); + *replyWaitRet = True; + fprintf(stderr, "dcop_process_message(): Returning\n"); +} + +/***************************************************************************/ + + Bool +dcop_send_signal( + const char * receiving_app, + const char * object, + const char * function, + char * data, + int dataLength +) +{ + char * pos = 0L; + char * header = 0L; + unsigned int headerLength = 0; + + struct DCOPMsg * pMsgPtr = 0; + + static const char* sAnonymous = "anonymous"; + + if (0 == dcop_ice_conn) { + fprintf(stderr, "Try running dcop_attach(), moron\n"); + return False; + } + + /* + * First let ICE initialize the ICE Message Header and give us a pointer to + * it (ICE manages that buffer internally) + */ + IceGetHeader( + dcop_ice_conn, + dcop_major_opcode, + DCOPSend, + sizeof(struct DCOPMsg), + struct DCOPMsg, + pMsgPtr + ); + + /* + * Marshall the arguments for the DCOP message header (callerApp, destApp, + * destObj, destFunc. The last argument is actually part of the data part of + * the call, but we add it to the header. It's the size of the marshalled + * argument data. In Qt it would look like QDataStream str( ... ) str << + * callerApp << destApp << destObj << destFun << + * argumentQByteArrayDataStuff; (where as str is the complete data stream + * sent do the dcopserver, excluding the ICE header) As the QByteArray is + * marshalled as [size][data] and as we (below) send the data in two chunks, + * first the dcop msg header and the the data, we just put the [size] field + * as last field into the dcop msg header ;-) + */ + + headerLength = strlen(sAnonymous) + 1 + + strlen(receiving_app) + 1 + + strlen(object) + 1 + + strlen(function) + 1 + + 4*5; /* 4 string lengths + 1 int */ + + header = (char *)malloc(headerLength); + + pos = header; + + pos = dcop_write_string(pos, sAnonymous); + pos = dcop_write_string(pos, receiving_app); + pos = dcop_write_string(pos, object); + pos = dcop_write_string(pos, function); + pos = dcop_write_int(pos, dataLength); + + headerLength = pos - header; + + pMsgPtr->key = dcop_key; + /* + * The length field tells the dcopserver how much bytes the dcop message + * takes up. We add that size to the already by IceGetHeader initialized + * length value, as it seems that under some circumstances (depending on the + * DCOPMsg structure size) the length field is aligned/padded. + */ + pMsgPtr->length += headerLength + dataLength; + + /* First let's send the dcop message header. + * IceSendData automatically takes care of first sending the Ice Message + * Header (outbufptr > outbuf -> flush the connection buffer) + */ + IceSendData(dcop_ice_conn, headerLength, header); + + /* Now the function argument data */ + IceSendData(dcop_ice_conn, dataLength, data); + + /* Send it all ;-) */ + IceFlush(dcop_ice_conn); + + free(header); + + if (IceConnectionStatus(dcop_ice_conn) != IceConnectAccepted) + return False; + + return True; +} + +/***************************************************************************/ + + Bool +dcop_call( + const char * appId, + const char * remApp, + const char * remObjId, + const char * remFun, + const char * data, + int dataLength, + char ** replyType, + char ** replyData, + int * replyDataLength +) +{ + IceReplyWaitInfo waitInfo; + IceProcessMessagesStatus status; + struct dcop_reply_struct replyStruct; + + char * pos = 0L; + char * outputData = 0L; + int outputDataLength = 0; + int temp = 0; + Bool success = False; + Bool readyRet = False; + + struct DCOPMsg * pMsg; + + + fprintf(stderr, "dcop_call() ...\n"); + + if (0 == dcop_ice_conn) { + fprintf(stderr, "Try running dcop_register(), moron\n"); + return False; + } + + + temp += strlen(appId); + temp += strlen(remApp); + temp += strlen(remObjId); + temp += strlen(remFun); + temp += dataLength; + temp += 1024; /* Extra space for marshalling overhead */ + + outputData = (char *)malloc(temp); + + temp = 0; + + pos = outputData; + pos = dcop_write_string(pos, appId); + pos = dcop_write_string(pos, remApp); + pos = dcop_write_string(pos, remObjId); + pos = dcop_write_string(pos, remFun); + pos = dcop_write_int(pos, dataLength); + + outputDataLength = pos - outputData; + + IceGetHeader( + dcop_ice_conn, + dcop_major_opcode, + DCOPCall, + sizeof(struct DCOPMsg), + struct DCOPMsg, + pMsg + ); + + pMsg->length += outputDataLength + dataLength; + + IceSendData(dcop_ice_conn, outputDataLength, outputData); + IceSendData(dcop_ice_conn, dataLength, (char *)data); + + IceFlush(dcop_ice_conn); + + free(outputData); + outputData = NULL; + + if (IceConnectionStatus(dcop_ice_conn) != IceConnectAccepted) { + fprintf(stderr, "dcop_call(): Connection not accepted\n"); + return False; + } + + waitInfo.sequence_of_request = IceLastSentSequenceNumber(dcop_ice_conn); + waitInfo.major_opcode_of_request = dcop_major_opcode; + waitInfo.minor_opcode_of_request = DCOPCall; + + replyStruct.status = DCOP_REPLY_PENDING; + replyStruct.replyId = dcop_reply_id++; + replyStruct.replyType = replyType; + replyStruct.replyData = replyData; + replyStruct.replyDataLength = replyDataLength; + + waitInfo.reply = (IcePointer)(&replyStruct); + + readyRet = False; + + do { + fprintf(stderr, "dcop_call(): Doing IceProcessMessages\n"); + status = IceProcessMessages(dcop_ice_conn, &waitInfo, &readyRet); + + if (status == IceProcessMessagesIOError) { + fprintf(stderr, "dcop_call(): IceProcessMessagesIOError\n"); + IceCloseConnection(dcop_ice_conn); + return False; + } + + fprintf(stderr, "dcop_call(): readyRet == %s\n", readyRet ? "True" : "False"); + } while (!readyRet); + + fprintf(stderr, "dcop_call(): Finished\n"); + return (replyStruct.status == DCOP_REPLY_OK) ? True : False; +} + +/***************************************************************************/ + + Bool +dcop_attach() +{ + fprintf(stderr, "dcop_attach()\n"); + return dcop_attach_internal(True); +} + + Bool +dcop_attach_internal(Bool register_as_anonymous) +{ + fprintf(stderr, + "dcop_attach_internal(%s)\n", register_as_anonymous ? "True" : "False"); + + if (False == dcop_ice_register()) return False; + if (False == dcop_connect()) return False; + if (False == dcop_protocol_setup()) return False; + + if (register_as_anonymous) + return (0L != dcop_register("anonymous", True)) ? True : False; + + return True; +} + +/***************************************************************************/ + + char * +dcop_register(const char * app_name, Bool add_pid) +{ + char * replyType = 0L; + char * replyData = 0L; + int replyLen = 0; + char * data = 0L; + char * pos = 0L; + int dataLength = 0; + Bool callStatus = False; + + fprintf(stderr, "dcop_register(`%s')\n", app_name); + + if (0 == dcop_app_name) { + + if (0 == dcop_ice_conn) + if (False == dcop_attach_internal(False)) + return 0L; + + } else { + + fprintf(stderr, "dcop_init(): Reregistering as `%s'\n", app_name); + + callStatus = dcop_detach(); + + if (False == callStatus) { + fprintf(stderr, "dcop_init(): Could not detach before reregistering\n"); + return 0L; + } + } + + if (False == add_pid) + dcop_requested_name = strdup(app_name); + + else { + + /* Leave room for "-pid" */ + int len = strlen(app_name) + 64; + dcop_requested_name = (char *)malloc(len); + + snprintf(dcop_requested_name, len, "%s-%ld", app_name, (long)getpid()); + } + + data = (char *)malloc(strlen(dcop_requested_name) + 42); + + pos = data; + pos = dcop_write_string(pos, dcop_requested_name); + dataLength = pos - data; + + callStatus = + dcop_call( + dcop_requested_name, + "DCOPServer", + "", /* Object irrelevant */ + "registerAs(QCString)", + data, + dataLength, + &replyType, + &replyData, + &replyLen + ); + + free(dcop_requested_name); + free(data); + + if (False == callStatus) { + fprintf(stderr, "dcop_register(): dcop_call() failed\n"); + return 0L; + } + + fprintf(stderr, "dcop_register(): Reply length is %d\n", replyLen); + + if (replyLen == 0) + return 0L; + + dcop_read_string(replyData, &dcop_app_name); + + return dcop_app_name; +} + +/***************************************************************************/ + + Bool +dcop_ice_register() +{ + dcop_major_opcode = + IceRegisterForProtocolSetup( + (char *)("DCOP"), + (char *)DCOPVendorString, + (char *)DCOPReleaseString, + 1, /* What's this ? */ + DCOPVersions, + DCOPAuthCount, + (char **)DCOPAuthNames, + DCOPClientAuthProcs, + 0L /* What's this ? */ + ); + + return (dcop_major_opcode >= 0) ? True : False; +} + + +/***************************************************************************/ + + Bool +dcop_connect() +{ + size_t bytesRead = 0; + IcePointer context = 0; + FILE * f = 0L; + char * newline = 0L; + char * homeDir = 0L; + char * display = 0L; + char * dcopServer = 0L; + char errBuf[BUFFER_SIZE]; + char fileName[BUFFER_SIZE]; + char hostName[BUFFER_SIZE]; + char displayName[BUFFER_SIZE]; + char * i; + + homeDir = getenv("HOME"); + + if (NULL == homeDir) + return False; + + display = getenv("DISPLAY"); + + if (NULL == display) + display = "NODISPLAY"; + + strncpy(displayName, display, sizeof(displayName)); + displayName[sizeof(displayName) - 1] = 0; + + if((i = strrchr(displayName, '.')) > strrchr(displayName, ':') && i) + *i = '\0'; + + while((i = strchr(displayName, ':')) != NULL) + *i = '_'; + + dcopServer = getenv("DCOPSERVER"); + + if (NULL == dcopServer) { + + if (-1 == chdir(homeDir)) { + fprintf(stderr, "Cannot cd ~\n"); + return False; + } + + hostName[0] = '\0'; + if (gethostname(hostName, sizeof(hostName))) + strcpy(hostName, "localhost"); + else + hostName[sizeof(hostName)-1] = '\0'; + + snprintf(fileName, sizeof(fileName), ".DCOPserver_%s_%s", hostName, displayName); + f = fopen(fileName, "r"); + + if (NULL == f) { + fprintf(stderr, "Cannot open ~/%s\n", fileName); + return False; + } + + dcopServer = (char *)malloc(BUFFER_SIZE); + + bytesRead = fread((void *)dcopServer, sizeof(char), BUFFER_SIZE, f); + dcopServer[BUFFER_SIZE - 1] = 0; + + if (0 == bytesRead) + return False; + + newline = strchr(dcopServer, '\n'); + + if (NULL == newline) { + fprintf(stderr, "dcop server file format invalid\n"); + return False; + } + + *newline = '\0'; + } + + dcop_ice_conn = + IceOpenConnection( + dcopServer, + context, + False, + dcop_major_opcode, + sizeof(errBuf), + errBuf + ); + + if (NULL != dcopServer) + free(dcopServer); + + if (0 == dcop_ice_conn) { + fprintf(stderr, "dcop_ice_conn is 0 :(\n"); + return False; + } + + IceSetShutdownNegotiation(dcop_ice_conn, False); + + return True; +} + +/***************************************************************************/ + + Bool +dcop_protocol_setup() +{ + char * vendor = 0L; + char * release = 0L; + IcePointer clientData = 0; + int majorVersion = 0; + int minorVersion = 0; + int status = 0; + char errBuf[BUFFER_SIZE]; + + status = + IceProtocolSetup( + dcop_ice_conn, + dcop_major_opcode, + clientData, + True, + &(majorVersion), + &(minorVersion), + &(vendor), + &(release), + BUFFER_SIZE, + errBuf + ); + + return ( + (status == IceProtocolSetupSuccess) && + (IceConnectionStatus(dcop_ice_conn) == IceConnectAccepted) + ); +} + +/***************************************************************************/ + + Bool +dcop_detach() +{ + int status; + IceProtocolShutdown(dcop_ice_conn, dcop_major_opcode); + status = IceCloseConnection(dcop_ice_conn); + + if (status == IceClosedNow) + dcop_ice_conn = 0L; + else + fprintf(stderr, "dcop_detach(): Could not detach\n"); + + return status == IceClosedNow; +} + |