/* * * C interface to DCOP * * (C) 2000 Rik Hemsley * (C) 2000 Simon Hausmann */ #include #include #include #include /* 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 #if defined Q_WS_X11 && ! defined K_WS_QTONLY #include #include #include #include #include #include #include #include #include #include #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 TQDataStream 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 TQByteArray 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(TQCString)", 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 (getenv("XAUTHLOCALHOSTNAME")) strlcpy(hostName, getenv("XAUTHLOCALHOSTNAME"),sizeof(hostName)-1); else 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; }