diff options
Diffstat (limited to 'dcopc/dcopc.c')
-rw-r--r-- | dcopc/dcopc.c | 1579 |
1 files changed, 1579 insertions, 0 deletions
diff --git a/dcopc/dcopc.c b/dcopc/dcopc.c new file mode 100644 index 00000000..fcb51e2b --- /dev/null +++ b/dcopc/dcopc.c @@ -0,0 +1,1579 @@ +/* +Copyright (c) 2000 Simon Hausmann <hausmann@kde.org> +Copyright (c) 2000 Lars Knoll <knoll@kde.org> +Copyright (c) 1999 Preston Brown <pbrown@kde.org> +Copyright (c) 1999, 2000 Matthias Ettrich <ettrich@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include <config.h> + +#include "global.h" +#include "dcopc.h" +#include "dcopobject.h" + +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + +typedef struct _DcopClientMessage DcopClientMessage; + +struct _DcopClientMessage +{ + int opcode; + CARD32 key; + dcop_data *data; +}; + +typedef struct _DcopClientPrivate DcopClientPrivate; + +struct _DcopClientPrivate +{ + gchar *app_id; + IceConn ice_conn; + gint major_opcode; + gint major_version, minor_version; + gchar *vendor, *release; + gboolean registered; + gchar *sender_id; + + gchar *default_object; + + CARD32 key; + CARD32 current_key; + + GList *transaction_list; + gboolean transaction; + gint32 transaction_id; + + int opcode; + + guint post_message_timer; + GList *messages; +}; + +struct _DcopClientTransaction +{ + gint32 id; + CARD32 key; + gchar *sender_id; +}; + +int DCOPAuthCount = 1; +const char *DCOPAuthNames[] = {"MIT-MAGIC-COOKIE-1"}; + +extern IcePoAuthStatus _IcePoMagicCookie1Proc (IceConn, void **, int, int, int, void *, int *, void **, char **); +extern IcePaAuthStatus _IcePaMagicCookie1Proc (IceConn, void **, int, int, void *, int *, void **, char **); + +IcePoAuthProc DCOPClientAuthProcs[] = {_IcePoMagicCookie1Proc}; +IcePaAuthProc DCOPServerAuthProcs[] = {_IcePaMagicCookie1Proc}; + +enum reply_status +{ Pending, Ok, Rejected, Failed }; + +typedef struct _reply_struct reply_struct; + +struct _reply_struct +{ + gint status; + gchar **reply_type; + dcop_data **reply_data; + guint reply_id; +}; + +static void reply_struct_init( reply_struct *s ) +{ + s->status = Pending; + s->reply_type = 0; + s->reply_data = 0; + s->reply_id = 0; +} + +#define P ((DcopClientPrivate *)(client->priv)) +#define ERR( emsg ) \ + { \ + if ( dcop_client_free_error_msg ) \ + g_free( dcop_client_error_msg ); \ + dcop_client_error_msg = (gchar *)emsg; \ + dcop_client_free_error_msg = FALSE; \ + } + +#define CLIENT_CLASS(obj) DCOP_CLIENT_CLASS(GTK_OBJECT(obj)->klass) + +static gchar *dcop_client_server_address = 0; +static gchar *dcop_client_error_msg = 0; +static gboolean dcop_client_free_error_msg; + +/* don't use the glib types for args here, as this is an ICE callback (Simon)*/ +void dcop_process_message( IceConn ice_conn, IcePointer client_object, + int opcode, unsigned long length, Bool swap, + IceReplyWaitInfo *replyWait, + Bool *replyWaitRet ); + +gchar *dcop_client_normalize_function_signature( const gchar *fun ); + +static IcePoVersionRec DCOPVersions[] = { + { DCOPVersionMajor, DCOPVersionMinor, dcop_process_message + } +}; + +static unsigned int dcop_client_count = 0; +static GtkObjectClass *parent_class = 0; + +static void dcop_client_class_init(DcopClientClass *klass); +static void dcop_client_init(DcopClient *client); + +static gboolean dcop_client_real_process ( DcopClient *client, const char *fun, dcop_data *data, + char **reply_type, dcop_data **reply_data ); + +GtkType +dcop_client_get_type(void) +{ + static GtkType dcop_client_type = 0; + if (!dcop_client_type) + { + static const GtkTypeInfo dcop_client_info = + { + (gchar *)"DcopClient", + sizeof(DcopClient), + sizeof(DcopClientClass), + (GtkClassInitFunc)dcop_client_class_init, + (GtkObjectInitFunc)dcop_client_init, + 0, + 0, + 0 + }; + dcop_client_type = gtk_type_unique(GTK_TYPE_OBJECT, &dcop_client_info); + } + return dcop_client_type; +} + +static void dcop_client_destroy( GtkObject *obj ); +static void dcop_init(void); +static void dcop_shutdown(void); + +void +dcop_client_class_init(DcopClientClass *klass) +{ + GtkObjectClass *object_class; + object_class = (GtkObjectClass *)klass; + + parent_class = (GtkObjectClass *)gtk_type_class(gtk_object_get_type()); + + object_class->destroy = dcop_client_destroy; + klass->process = dcop_client_real_process; +} + +void +dcop_client_init(DcopClient *client) +{ + DcopClientPrivate *p; +/* tststs :-) C++ hackers :-) (Simon)*/ +/* p = new GtkDcopClientPrivate;*/ + p = g_new( DcopClientPrivate, 1 ); + + p->app_id = 0; + p->ice_conn = 0; + p->major_opcode = p->major_version = p->minor_version = 0; + p->vendor = p->release = 0; + p->registered = FALSE; + p->sender_id = 0; + p->key = 0; + p->current_key = 0; + p->post_message_timer = 0; + p->messages = 0; + p->default_object = 0; + p->transaction_list = 0; + p->transaction = FALSE; + p->transaction_id = 0; + p->opcode = 0; + + client->priv = p; + + if ( dcop_client_count == 0 ) + dcop_init(); + + dcop_client_count++; +} + +void dcop_client_destroy( GtkObject *obj ) +{ + DcopClient *client = DCOP_CLIENT(obj); + + g_message( "dcop_client_destructor()\n" ); + + if ( P->ice_conn && IceConnectionStatus( P->ice_conn ) == IceConnectAccepted ) + dcop_client_detach( client ); + + if ( P->post_message_timer != 0 ) + g_source_remove( P->post_message_timer ); + + { + GList *it = g_list_first( P->messages ); + while ( it ) + { + DcopClientMessage *msg = (DcopClientMessage *)it->data; + dcop_data_deref( msg->data ); + g_free( msg ); + it = g_list_next( it ); + } + g_list_free( P->messages ); + } + + { + GList *it = g_list_first( P->transaction_list ); + while ( it ) + { + g_free( ((DcopClientTransaction *)it->data)->sender_id ); + g_free( (DcopClientTransaction *)it ); + it = g_list_next( it ); + } + + g_list_free( P->transaction_list ); + } + + g_free( P->app_id ); + g_free( P->vendor ); + g_free( P->release ); + g_free( P->sender_id ); + + g_free( P ); + + if ( !--dcop_client_count ) + dcop_shutdown(); + + parent_class->destroy(GTK_OBJECT(client)); +} + + +void dcop_init() +{ + g_message( "dcop_init\n" ); + dcop_client_free_error_msg = FALSE; +} + +void dcop_shutdown() +{ + g_message( "dcop_shutdown\n" ); + dcop_free( dcop_client_server_address ); + + if ( dcop_client_free_error_msg ) + dcop_free( dcop_client_error_msg ); +} + +DcopClient *dcop_client_new() +{ + return (DcopClient *) gtk_type_new(dcop_client_get_type()); +} + +void dcop_client_set_server_address( const gchar *addr ) +{ + dcop_string_copy( dcop_client_server_address, addr ); +} + +/* SM DUMMY */ +#include <X11/SM/SMlib.h> +static gboolean HostBasedAuthProc ( char* hostname) +{ + /* we don't need any security here, as this is only a hack to + * get the protocol number we want for DCOP. We don't use the SM + * connection in any way */ + return True; +} + +static Status NewClientProc ( SmsConn c, SmPointer p, unsigned long*l, SmsCallbacks*cb, char**s ) +{ + return 0; +} + +static void dcop_client_registerXSM() +{ + char errormsg[256]; + if (!SmsInitialize ((char *)("SAMPLE-SM"), (char *)("1.0"), + NewClientProc, NULL, + HostBasedAuthProc, 256, errormsg)) + { + g_error( "register xsm failed"); + exit( 1 ); + } +} + +/* hack*/ +extern int _IceLastMajorOpcode; + +static void dcop_client_xsm_check() +{ + if ( _IceLastMajorOpcode < 1 ) + /* hack to enforce the correct ICE major opcode for DCOP*/ + dcop_client_registerXSM(); +} + +static gboolean dcop_client_attach_internal( DcopClient *client, gboolean anonymous ) +{ + gboolean bClearServerAddr = FALSE; + gchar *fname = 0; + const gchar *dcopSrvEnv = 0; + gchar *dcopSrv = 0; + gchar *dcopServerFileContent = 0; + glong dcopServerFileContentSize = 0; + ssize_t bytesRead = 0; + gchar *newLine = 0; + FILE *f = 0; + gchar errBuf[1024]; + gint setupstat; + + g_message( "dcop_client_attach\n" ); + + if ( dcop_client_is_attached( client ) ) + dcop_client_detach( client ); + + dcop_client_xsm_check(); + + if ( ( P->major_opcode = IceRegisterForProtocolSetup( (char *)("DCOP"), + (char *)(DCOPVendorString), + (char *)(DCOPReleaseString), + 1, DCOPVersions, + DCOPAuthCount, + (char **)(DCOPAuthNames), + DCOPClientAuthProcs, 0 ) ) < 0 ) + { + ERR( "Communications could not be established." ); + return FALSE; + } + else + { + g_message( "dcop: major opcode is %d\n", P->major_opcode ); + } + + if ( !dcop_client_server_address ) + { + dcopSrvEnv = getenv( "DCOPSERVER" ); + if ( !dcopSrvEnv || strlen( dcopSrvEnv ) == 0 ) + { + char hostName[255]; + + fname = g_strdup( getenv( "HOME" ) ); + fname = (gchar *)g_realloc( fname, strlen( fname ) + 1 + 13 ); + strcat( fname, "/.DCOPserver_" ); + + if ( gethostname( hostName, 255 ) == 0 ) + { + fname = (gchar *)g_realloc( fname, strlen( fname ) + 1 + strlen( hostName ) ); + strcat( fname, hostName ); + } + else + { + fname = (gchar *)g_realloc( fname, strlen( fname ) + 1 + 9 ); + strcat( fname, "localhost" ); + } + + f = fopen( fname, "r" ); + + if ( f == NULL ) /* try .DCOPServer_hostname_display (for KDE > 2.0) */ + { + char *i; + char *display = getenv( "DISPLAY" ); + gchar *display_real = g_strdup( display ); + gchar *disppos; + + /* dcopserver per display, not per screen */ + if ( ( disppos = strchr( display_real, '.' ) ) > strchr( display_real, ':' ) && disppos != NULL ) + (*disppos) = '\0'; + + while((i = strchr(display_real, ':')) != NULL) + *i = '_'; + + fname = (gchar *)g_realloc( fname, strlen( fname ) + 1 + 1 + strlen( display_real ) ); + strcat( fname, "_" ); + strcat( fname, display_real ); + + g_free( display_real ); + + f = fopen( fname, "r" ); + + if ( f == NULL ) + { + g_free( fname ); + ERR( "Cannot open ~/.DCOPserver" ); + return FALSE; + } + } + + fseek( f, 0L, SEEK_END ); + + dcopServerFileContentSize = ftell( f ); + + if ( dcopServerFileContentSize == 0L ) + { + g_free( fname ); + ERR( "Invalid ~/.DCOPserver" ); + return FALSE; + } + + fseek( f, 0L, SEEK_SET ); + + dcopServerFileContent = (gchar *)g_malloc( dcopServerFileContentSize ); + + bytesRead = fread( (void *)dcopServerFileContent, sizeof(gchar), dcopServerFileContentSize, f ); + + if ( bytesRead != dcopServerFileContentSize ) + { + g_free( fname ); + g_free( dcopServerFileContent ); + fclose( f ); + ERR( "Cannot read ~/.DCOPserver" ); + return FALSE; + } + + newLine = strchr( dcopServerFileContent, '\n' ); + + if ( newLine == NULL ) + { + g_free( fname ); + g_free( dcopServerFileContent ); + fclose( f ); + ERR( "Invalid ~/.DCOPserver" ); + return FALSE; + } + + *newLine = '\0'; + + fclose( f ); + g_free( fname ); + + dcopSrv = g_strdup( dcopServerFileContent ); + + g_free( dcopServerFileContent ); + } + else + dcopSrv = g_strdup( dcopSrvEnv ); + + if ( dcopSrv == NULL ) + { + ERR( "Cannot determine dcop server address." ); + return FALSE; + } + + g_message( "dcop server address is : %s\n", dcopSrv ); + + dcop_client_server_address = dcopSrv; + + bClearServerAddr = TRUE; + + } + + if ( ( P->ice_conn = IceOpenConnection( (char *)( dcop_client_server_address ), + (IcePointer)( client ), False, P->major_opcode, + sizeof( errBuf ), errBuf ) ) == NULL ) + { + if ( bClearServerAddr ) + dcop_free( dcop_client_server_address ); + ERR( g_strdup( errBuf ) ); + dcop_client_free_error_msg = TRUE; + return FALSE; + + } + + IceSetShutdownNegotiation( P->ice_conn, False ); + + setupstat = IceProtocolSetup( P->ice_conn, P->major_opcode, (IcePointer *)client, False, + &(P->major_version), &(P->minor_version), + &(P->vendor), &(P->release), sizeof( errBuf ), errBuf ); + + if ( setupstat == IceProtocolSetupFailure || + setupstat == IceProtocolSetupIOError ) + { + IceCloseConnection( P->ice_conn ); + + if ( bClearServerAddr ) + dcop_free( dcop_client_server_address ); + + ERR( g_strdup( errBuf ) ); + dcop_client_free_error_msg = TRUE; + return FALSE; + } + else if ( setupstat == IceProtocolAlreadyActive ) + { + if ( bClearServerAddr ) + dcop_free( dcop_client_server_address ); + + ERR( "Internal error in IceOpenConnection" ); + return FALSE; + } + + if ( IceConnectionStatus( P->ice_conn ) != IceConnectAccepted ) + { + if ( bClearServerAddr ) + dcop_free( dcop_client_server_address ); + + ERR( "DCOP server did not accept the connection" ); + return FALSE; + } + + if ( anonymous ) + dcop_client_register_as( client, "anonymous", TRUE ); + + return TRUE; +} + +gboolean dcop_client_attach( DcopClient *client ) +{ + if ( !dcop_client_attach_internal( client, TRUE ) ) + if ( !dcop_client_attach_internal( client, TRUE ) ) + return FALSE; /* try two times!*/ + + return TRUE; +} + +gboolean dcop_client_detach( DcopClient *client ) +{ + int status; + + g_message( "dcop_client_detach\n" ); + + if ( P->ice_conn ) + { + IceProtocolShutdown( P->ice_conn, P->major_opcode ); + status = IceCloseConnection( P->ice_conn ); + if ( status != IceClosedNow ) + { + ERR( "error detaching from DCOP server" ); + return FALSE; + } + else + P->ice_conn = 0L; + } + + P->registered = FALSE; + + return TRUE; +} + +gboolean dcop_client_is_attached( DcopClient *client ) +{ + if ( !P->ice_conn ) + return FALSE; + + return (IceConnectionStatus( P->ice_conn ) == IceConnectAccepted ); +} + +const gchar *dcop_client_register_as( DcopClient *client, const gchar *app_id, gboolean add_pid /* = TRUE */ ) +{ + gchar *result = 0; + + gchar *id = g_strdup( app_id ); + gchar pid[64]; + gint pid_len = 0; + + dcop_data *data = 0; + gchar *reply_type = 0; + dcop_data *reply_data = 0; + + g_message( "dcop_client_register_as %s\n", app_id ); + + if ( dcop_client_is_registered( client ) ) + dcop_client_detach( client ); + + if ( !dcop_client_is_attached( client ) ) + if ( !dcop_client_attach_internal( client, FALSE ) ) + return result; /*try two times*/ + + if ( add_pid ) + { + pid_len = g_snprintf( pid, sizeof( pid ), "-%d", getpid() ); + id = (gchar *)g_realloc( id, strlen( id ) + 1 + pid_len ); + strcat( id, pid ); + } + + + g_message( "trying to register as %s\n", id ); + + data = dcop_data_ref( dcop_data_new() ); + + dcop_marshal_string( data, id ); + + if ( dcop_client_call( client, "DCOPServer", "", "registerAs(QCString)", data, + &reply_type, + &reply_data ) ) + { + dcop_data_reset( reply_data ); + dcop_demarshal_string( reply_data, &result ); + + dcop_data_deref( reply_data ); + g_free( reply_type ); + } + + dcop_string_assign( P->app_id, result ); + + P->registered = ( P->app_id != NULL && strlen( P->app_id ) > 0 ); + + if ( P->registered ) + g_message( "we are registered as %s\n", P->app_id ); + else + g_message( "registration failed\n" ); + + dcop_data_deref( data ); + + g_free( id ); + + return result; +} + +gboolean dcop_client_is_registered( DcopClient *client ) +{ + return P->registered; +} + +const gchar *dcop_client_app_id( DcopClient *client ) +{ + return P->app_id; +} + +int dcop_client_socket( DcopClient *client ) +{ + if ( P->ice_conn ) + return IceConnectionNumber( P->ice_conn ); + else + return 0; +} + +gboolean dcop_client_send( DcopClient *client, + const gchar *rem_app, const gchar *rem_obj, const gchar *rem_fun, + dcop_data *data ) +{ + struct DCOPMsg *pMsg = 0; + dcop_data *hdata = 0; + gchar *func = 0; + gboolean res = TRUE; + + g_message( "dcop_client_send( %s, %s, %s )\n", rem_app, rem_obj, rem_fun); + + if ( !dcop_client_is_attached( client ) ) + return FALSE; + + if ( strcmp( P->app_id, rem_app ) == 0 ) + { + gchar *reply_type = 0; + dcop_data *reply_data = 0; + + if ( !dcop_client_receive( client, rem_app, rem_obj, rem_fun, data, &reply_type, &reply_data ) ) + g_warning( "dcop failure in app %s:\n object '%s' has no function '%s'", rem_app, rem_obj, rem_fun ); + + return TRUE; + } + + hdata = dcop_data_ref( dcop_data_new() ); + + dcop_marshal_string( hdata, P->app_id ); + dcop_marshal_string( hdata, rem_app ); + dcop_marshal_string( hdata, rem_obj ); + + func = dcop_client_normalize_function_signature( rem_fun ); + dcop_marshal_string( hdata, func ); + + dcop_marshal_uint32( hdata, data->size ); + + IceGetHeader( P->ice_conn, P->major_opcode, DCOPSend, sizeof(struct DCOPMsg), struct DCOPMsg, pMsg ); + + pMsg->key = 1; /* DCOPSend always uses the magic key 1*/ + pMsg->length += hdata->size + data->size; + + IceSendData( P->ice_conn, hdata->size, hdata->ptr ); + IceSendData( P->ice_conn, data->size, data->ptr ); + + /* IceFlush( P->ice_conn ); + */ + if ( IceConnectionStatus( P->ice_conn ) != IceConnectAccepted ) + res = FALSE; + + g_free( func ); + + return res; +} + +static gboolean dcop_client_call_internal( DcopClient *client, + const gchar *rem_app, const gchar *rem_obj, const gchar *rem_fun, + dcop_data *data, + gchar **reply_type, dcop_data **reply_data, + gint minor_opcode ) +{ + gboolean success = FALSE; + struct DCOPMsg *pMsg = 0; + dcop_data *hdata = 0; + gchar *func = 0; + IceReplyWaitInfo waitInfo; + reply_struct reply; + gboolean readyRet; + IceProcessMessagesStatus status; + CARD32 old_current_key = 0; + + reply_struct_init( &reply ); + + if ( !dcop_client_is_attached( client ) ) + return FALSE; + + old_current_key = P->current_key; + if ( !P->current_key ) + P->current_key = P->key; /* no key, yet, initiate new call*/ + + hdata = dcop_data_ref( dcop_data_new() ); + + dcop_marshal_string( hdata, P->app_id ); + dcop_marshal_string( hdata, rem_app ); + dcop_marshal_string( hdata, rem_obj ); + + func = dcop_client_normalize_function_signature( rem_fun ); + dcop_marshal_string( hdata, func ); + + dcop_marshal_uint32( hdata, data->size ); + + IceGetHeader( P->ice_conn, P->major_opcode, minor_opcode, + sizeof(struct DCOPMsg), struct DCOPMsg, pMsg ); + + pMsg->key = P->current_key; + pMsg->length += hdata->size + data->size; + + IceSendData( P->ice_conn, hdata->size, hdata->ptr ); + IceSendData( P->ice_conn, data->size, data->ptr ); + + if ( IceConnectionStatus( P->ice_conn ) != IceConnectAccepted ) + { + dcop_data_deref( hdata ); + g_free( func ); + P->current_key = old_current_key; + return FALSE; + } + + IceFlush( P->ice_conn ); + + waitInfo.sequence_of_request = IceLastSentSequenceNumber( P->ice_conn ); + waitInfo.major_opcode_of_request = P->major_opcode; + waitInfo.minor_opcode_of_request = minor_opcode; + reply.reply_type = reply_type; + reply.reply_data = reply_data; + waitInfo.reply = (IcePointer)&reply; + + readyRet = False; + + do + { + status = IceProcessMessages( P->ice_conn, &waitInfo, &readyRet ); + if ( status == IceProcessMessagesIOError ) + { + IceCloseConnection( P->ice_conn ); + dcop_data_deref( hdata ); + g_free( func ); + P->current_key = old_current_key; + return FALSE; + } + } while ( !readyRet ); + + dcop_data_deref( hdata ); + + success = reply.status == Ok; + + g_free( func ); + + P->current_key = old_current_key; + return success; +} + +gboolean dcop_client_call( DcopClient *client, + const gchar *rem_app, const gchar *rem_obj, const gchar *rem_fun, + dcop_data *data, + gchar **reply_type, dcop_data **reply_data ) +{ + return dcop_client_call_internal( client, rem_app, rem_obj, rem_fun, data, + reply_type, reply_data, DCOPCall ); +} + +gboolean dcop_client_is_application_registered( DcopClient *client, const gchar *app ) +{ + gchar *reply_type = 0; + dcop_data *reply_data = 0; + dcop_data *data = dcop_data_ref( dcop_data_new() ); + gboolean res = FALSE; + + dcop_marshal_string( data, app ); + + if ( dcop_client_call( client, "DCOPServer", "", "isApplicationRegistered(QCString)", data, &reply_type, &reply_data ) ) + { + dcop_data_reset( reply_data ); + dcop_demarshal_boolean( reply_data, &res ); + } + + g_free( reply_type ); + if ( reply_data ) + dcop_data_deref( reply_data ); + dcop_data_deref( data ); + + return res; +} + +GList *dcop_client_registered_applications( DcopClient *client ) +{ + GList *res = 0; + dcop_data *data = 0; + dcop_data *reply_data = 0; + gchar *reply_type = 0; + + data = dcop_data_ref( dcop_data_new() ); + + if ( dcop_client_call( client, "DCOPServer", "", "registeredApplications()", data, + &reply_type, &reply_data ) ) + { + fprintf( stderr, "reply type is %s\n", reply_type ); + dcop_data_reset( reply_data ); + dcop_demarshal_stringlist( reply_data, &res ); + dcop_data_deref( reply_data ); + } + + g_free( reply_type ); + dcop_data_deref( data ); + return res; +} + +extern GHashTable *object_dict; + +GList *g_temp_object_list = 0; + +static void dcop_client_receive_list_objects_internal( gpointer key, gpointer val, gpointer user_data ) +{ + gchar *nam = (gchar *)key; + DcopObject *obj = (DcopObject *)val; + DcopClient *client = (DcopClient *)user_data; + const gchar *id = DCOP_ID( obj ); + + if ( id && strlen( id ) > 0 ) + { + if ( P->default_object && + strcmp( id, P->default_object ) == 0 ) + g_temp_object_list = g_list_append( g_temp_object_list, (gchar *)"default" ); + + g_temp_object_list = g_list_append( g_temp_object_list, (gchar *)id ); + } +} + +gboolean dcop_client_receive( DcopClient *client, + const gchar *app, const gchar *obj, const gchar *fun, + dcop_data *data, + gchar **reply_type, dcop_data **reply_data ) +{ + DcopObject *o; + + g_message( "dcop_client_receive app=%s obj=%s fun=%s\n", app, obj, fun); + + if ( obj && strcmp( obj, "DCOPClient" ) == 0 ) + { + if ( strcmp( fun, "objects()" ) == 0 ) + { + GList *list = 0; + + *reply_type = strdup( "QCStringList" ); + *reply_data = dcop_data_ref( dcop_data_new() ); + + if ( object_dict ) + { + /* + GList *it = g_list_first( object_list ); + + while ( it ) + { + if ( it->data ) + { + const gchar *id; + o = (DcopObject *)it->data; + id = DCOP_ID(o); + if ( id && strlen( id ) > 0 ) + { + if ( P->default_object && + strcmp( id, P->default_object ) == 0 ) + list = g_list_append( list, (gchar *)"default" ); + + list = g_list_append( list, (gchar *)id ); + } + } + it = g_list_next( it ); + } + */ + g_temp_object_list = 0; + + g_hash_table_foreach( object_dict, dcop_client_receive_list_objects_internal, client ); + + list = g_temp_object_list; + } + + dcop_marshal_stringlist( *reply_data, list ); + /* important: only "free" (return to internal allocator) the list*/ + /* itself, not it's data, as that data are either static strings*/ + /* or strings already owned by someone else*/ + g_list_free( list ); + return TRUE; + } + + if ( CLIENT_CLASS(client)->process( client, fun, data, reply_type, reply_data ) ) + return TRUE; + } + + if ( !obj || strlen( obj ) == 0 || strcmp( obj, "default" ) == 0 ) + if ( P->default_object && strlen( P->default_object ) != 0 ) + { + o = dcop_object_lookup( P->default_object ); + if ( o && DCOP_OBJECT_CLASS(GTK_OBJECT(o)->klass)->process( o, fun, data, reply_type, reply_data ) ) + return TRUE; + } + + if ( obj && obj[ strlen( obj ) - 1 ] == '*' ) + { + gchar *partial_id = (gchar *)g_malloc( strlen( obj ) - 1 ); + GList *match_list = 0; + + strncpy( partial_id, obj, strlen( obj ) - 1 ); + + match_list = dcop_object_match( partial_id ); + + if ( match_list ) + { + GList *it = g_list_first( match_list ); + + while ( it ) + { + o = (DcopObject *)it->data; + + if ( !DCOP_OBJECT_CLASS(GTK_OBJECT(o)->klass)->process( o, fun, data, reply_type, reply_data ) ) + { + g_list_free( match_list ); + g_free( partial_id ); + return FALSE; + } + + it = g_list_next( it ); + } + + g_list_free( match_list ); + } + + g_free( partial_id ); + return TRUE; + } + + /* ### proxy support + */ + o = dcop_object_lookup( obj ); + if ( !o ) + return FALSE; + return DCOP_OBJECT_CLASS(GTK_OBJECT(o)->klass)->process( o, fun, data, reply_type, reply_data ); +} + +static inline gboolean is_ident_char( gchar x ) +{ /* Avoid bug in isalnum */ + return x == '_' || (x >= '0' && x <= '9') || + (x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z'); +} + +gchar *dcop_client_normalize_function_signature( const gchar *fun ) +{ + gchar *result; + size_t len = strlen( fun ); + const gchar *from; + gchar *to, *first; + gchar last; + + if ( len == 0 ) + return g_strdup( fun ); + + result = (gchar *)g_malloc( len + 1 ); + + from = fun; + first = to = result; + last = 0; + + while ( 42 ) + { + while ( *from && isspace( *from ) ) + from++; + if ( last && is_ident_char( last ) && is_ident_char( *from ) ) + *to++ = 0x20; + while ( *from && !isspace( *from ) ) + { + last = *from++; + *to++ = last; + } + if ( !*from ) + break; + } + if ( to > first && *(to-1) == 0x20 ) + to--; + *to = '\0'; + result = (gchar *)g_realloc( result, (int)((long)to - (long)result) + 1 ); + return result; +} + +const gchar *dcop_client_sender_id( DcopClient *client ) +{ + return P->sender_id; +} + +gboolean dcop_client_process( DcopClient *client, const gchar *fun, dcop_data *data, + gchar **reply_type, dcop_data **reply_data ) +{ + return CLIENT_CLASS( client )->process( client, fun, data, reply_type, reply_data ); +} + +gboolean dcop_client_real_process( DcopClient *client, const gchar *fun, dcop_data *data, + gchar **reply_type, dcop_data **reply_data ) +{ + /* empty default implementation*/ + return FALSE; +} + +void dcop_client_process_socket_data( DcopClient *client ) +{ + IceProcessMessagesStatus status; + + g_message( "dcop_client_process_socket_data\n" ); + + status = IceProcessMessages( P->ice_conn, 0, 0 ); + + g_message( "dcop_client_process_socket_data : messages processed\n" ); + + if ( status == IceProcessMessagesIOError ) + { + IceCloseConnection( P->ice_conn ); + g_message( "error receiving data from the DCOP server.\n" ); + return; + } +} + +const gchar *dcop_client_error_message() +{ + return dcop_client_error_msg; +} + +void dcop_process_internal( DcopClient *client, int opcode, CARD32 key, dcop_data *data_received, gboolean can_post ); + +/* don't use the glib types for args here, as this is an ICE callback (Simon)*/ +void dcop_process_message( IceConn ice_conn, IcePointer client_object, + int opcode, unsigned long length, Bool swap, + IceReplyWaitInfo *replyWait, + Bool *replyWaitRet ) +{ + struct DCOPMsg *pMsg = 0; + DcopClient *client = (DcopClient *)client_object; + void *data_received; + dcop_data *tmp_stream = 0; + unsigned int id; + char *called_app = 0; + char *app = 0; + char *obj = 0; + char *fun = 0; + CARD32 key; + + IceReadMessageHeader( ice_conn, sizeof( struct DCOPMsg ), struct DCOPMsg, pMsg ); + + key = pMsg->key; + if ( P->key == 0 ) + P->key = key; /* received a key from the server*/ + + data_received = g_malloc( length ); + IceReadData( ice_conn, length, data_received ); + + tmp_stream = dcop_data_ref( dcop_data_new() ); + dcop_data_assign( tmp_stream, (char *)data_received, length ); + + P->opcode = opcode; + + switch ( opcode ) + { + case DCOPReplyFailed: + if ( replyWait ) + { + ((reply_struct *)replyWait->reply)->status = Failed; + *replyWaitRet = True; + break; + } + else + { + g_warning( "dcop error: received an unexpected DCOPReplyFailed opcode.\n" ); + break; + } + case DCOPReply: + if ( replyWait ) + { + ((reply_struct *)replyWait->reply)->status = Ok; + + dcop_demarshal_string( tmp_stream, &called_app ); + dcop_demarshal_string( tmp_stream, &app ); + dcop_demarshal_string( tmp_stream, ((reply_struct *)replyWait->reply)->reply_type ); + dcop_demarshal_data( tmp_stream, ((reply_struct *)replyWait->reply)->reply_data ); + + *replyWaitRet = True; + break; + } + else + { + g_warning( "dcop error: received an unexpected DCOPReply.\n" ); + break; + } + case DCOPReplyWait: + if ( replyWait ) + { + dcop_demarshal_string( tmp_stream, &called_app ); + dcop_demarshal_string( tmp_stream, &app ); + dcop_demarshal_uint32( tmp_stream, &id ); + + ((reply_struct *)replyWait->reply)->reply_id = id; + + break; + } + else + { + g_warning( "dcop error: received an unexpected DCOPReplyWait.\n" ); + break; + } + case DCOPReplyDelayed: + if ( replyWait ) + { + ((reply_struct *)replyWait->reply)->status = Ok; + + dcop_demarshal_string( tmp_stream, &called_app ); + dcop_demarshal_string( tmp_stream, &app ); + dcop_demarshal_uint32( tmp_stream, &id ); + dcop_demarshal_string( tmp_stream, ((reply_struct *)replyWait->reply)->reply_type ); + dcop_demarshal_data( tmp_stream, ((reply_struct *)replyWait->reply)->reply_data ); + + if ( id != ((reply_struct *)replyWait->reply)->reply_id ) + { + ((reply_struct *)replyWait->reply)->status = Failed; + g_warning( "dcop error: received a wrong sequence id for DCOPReplyDelayed.\n" ); + } + + *replyWaitRet = True; + + break; + } + else + { + g_message( "dcop error: received an unexpeced DCOPReplyDelayed.\n" ); + break; + } + case DCOPCall: + case DCOPFind: + case DCOPSend: + dcop_process_internal( client, opcode, key, tmp_stream, TRUE ); + break; + } + + dcop_data_deref( tmp_stream ); + + g_free( called_app ); + g_free( app ); +} + +static gboolean dcop_client_process_post_messages_internal( gpointer data ) +{ + DcopClient *client = DCOP_CLIENT( data ); + GList *it = 0; + + g_message( "dcop_client_process_post_messages_internal" ); + + if ( g_list_length( P->messages ) == 0 ) + return FALSE; + + it = g_list_first( P->messages ); + while ( it ) + { + DcopClientMessage *msg = (DcopClientMessage *)it->data; + it = g_list_next( it ); + + g_assert( msg ); + + if ( P->current_key && msg->key != P->current_key ) + continue; + + P->messages = g_list_remove( P->messages, msg ); + dcop_process_internal( client, msg->opcode, msg->key, msg->data, FALSE ); + dcop_data_deref( msg->data ); + g_free( msg ); + } + + if ( g_list_length( P->messages ) == 0 ) + return FALSE; + + return TRUE; +} + +void dcop_process_internal( DcopClient *client, int opcode, CARD32 key, dcop_data *data_received, gboolean can_post ) +{ + gchar *app = 0; + gchar *obj = 0; + gchar *fun = 0; + char *reply_type = 0; + dcop_data *reply_data = 0; + gboolean b = FALSE; + CARD32 old_current_key = 0; + dcop_data *data = 0; + dcop_data *reply = 0; + struct DCOPMsg *pMsg = 0; + + dcop_free( P->sender_id ); + dcop_demarshal_string( data_received, &P->sender_id ); + dcop_demarshal_string( data_received, &app ); + dcop_demarshal_string( data_received, &obj ); + dcop_demarshal_string( data_received, &fun ); + dcop_demarshal_data( data_received, &data ); + + if ( can_post && P->current_key && key != P->current_key ) + { + DcopClientMessage *msg = g_new( DcopClientMessage, 1 ); + g_message( "posting message with key %i", key ); + msg->opcode = opcode; + msg->key = key; + msg->data = dcop_data_ref( data_received ); + P->messages = g_list_append( P->messages, msg ); + if ( P->post_message_timer != 0 ) + g_source_remove( P->post_message_timer ); + P->post_message_timer = g_timeout_add( 0, dcop_client_process_post_messages_internal, client ); + return; + } + + old_current_key = P->current_key; + if ( opcode != DCOPSend ) /* DCOPSend doesn't change the current key*/ + P->current_key = key; + + if ( opcode == DCOPFind ) + { + /* #### b = dcop_client_find( app, obj, fun, data, &reply_type, &reply_data ); */ + } + else + b = dcop_client_receive( client, app, obj, fun, data, &reply_type, &reply_data ); + + if ( opcode == DCOPSend ) + { + g_free( app ); + g_free( obj ); + g_free( fun ); + if ( data ) + dcop_data_deref( data ); + g_free( reply_type ); + if ( reply_data ) + dcop_data_deref( reply_data ); + return; + } + + P->current_key = old_current_key; + + reply = dcop_data_ref( dcop_data_new() ); + + if ( P->transaction_id ) + { + dcop_marshal_string( reply, P->app_id ); + dcop_marshal_string( reply, P->sender_id ); + dcop_marshal_uint32( reply, (guint32)P->transaction_id ); + + IceGetHeader( P->ice_conn, P->major_opcode, DCOPReplyWait, + sizeof( struct DCOPMsg ), struct DCOPMsg, pMsg ); + + pMsg->key = key; + pMsg->length += reply->size; + + IceSendData( P->ice_conn, reply->size, reply->ptr ); + + dcop_data_deref( reply ); + g_free( app ); + g_free( obj ); + g_free( fun ); + dcop_data_deref( data ); + return; + } + + if ( !b ) + { + g_warning( "dcop failure in app %s:\n object '%s' has no function '%s'", app, obj, fun ); + + dcop_marshal_string( reply, P->app_id ); + dcop_marshal_string( reply, P->sender_id ); + + IceGetHeader( P->ice_conn, P->major_opcode, DCOPReplyFailed, + sizeof( struct DCOPMsg ), struct DCOPMsg, pMsg ); + pMsg->key = key; + pMsg->length += reply->size; + IceSendData( P->ice_conn, reply->size, reply->ptr ); + + dcop_data_deref( reply ); + g_free( app ); + g_free( obj ); + g_free( fun ); + dcop_data_deref( data ); + return; + } + + if ( !reply_data ) + reply_data = dcop_data_ref( dcop_data_new() ); + + dcop_marshal_string( reply, P->app_id ); + dcop_marshal_string( reply, P->sender_id ); + dcop_marshal_string( reply, reply_type ); + dcop_marshal_uint32( reply, reply_data->size ); + + IceGetHeader( P->ice_conn, P->major_opcode, DCOPReply, + sizeof( struct DCOPMsg ), struct DCOPMsg, pMsg ); + + pMsg->key = key; + pMsg->length += reply->size + reply_data->size; + /* use IceSendData not IceWriteData to avoid a copy. Output buffer + shouldn't need to be flushed. */ + IceSendData( P->ice_conn, reply->size, reply->ptr ); + IceSendData( P->ice_conn, reply_data->size, reply_data->ptr ); + + dcop_data_deref( reply ); + g_free( app ); + g_free( obj ); + g_free( fun ); + dcop_data_deref( data ); + dcop_data_deref( reply_data ); +} + +void dcop_client_set_default_object( DcopClient *client, const gchar *obj_id ) +{ + dcop_string_copy( P->default_object, obj_id ); +} + +const gchar *dcop_client_default_object( DcopClient *client ) +{ + return P->default_object; +} + +DcopClientTransaction *dcop_client_begin_transaction( DcopClient *client ) +{ + DcopClientTransaction *transaction = 0; + + if ( P->opcode == DCOPSend ) + return 0; + + P->transaction = TRUE; + transaction = g_new( DcopClientTransaction, 1 ); + transaction->sender_id = g_strdup( P->sender_id ); + + if ( !P->transaction_id ) + P->transaction_id++; + + transaction->id = ++(P->transaction_id); + transaction->key = P->current_key; + + P->transaction_list = g_list_append( P->transaction_list, transaction ); + + return transaction; +} + +gint32 dcop_client_transaction_id( DcopClient *client ) +{ + if ( P->transaction ) + return P->transaction_id; + else + return 0; +} + +void dcop_client_end_transaction( DcopClient *client, DcopClientTransaction *trans, gchar *reply_type, dcop_data *reply_data ) +{ + struct DCOPMsg *pMsg = 0; + dcop_data *data = 0; + + if ( !trans ) + return; + + if ( !dcop_client_is_attached( client ) ) + return; + + if ( !P->transaction_list ) + { + g_warning( "dcop_client_end_transaction: no pending transactions!" ); + return; + } + + if ( !g_list_find( P->transaction_list, trans ) ) + { + g_warning( "dcop_client_end_transaction: unknown transaction!" ); + return; + } + + P->transaction_list = g_list_remove( P->transaction_list, trans ); + + data = dcop_data_ref( dcop_data_new() ); + + dcop_data_ref( reply_data ); + + dcop_marshal_string( data, P->app_id ); + dcop_marshal_string( data, trans->sender_id ); + dcop_marshal_uint32( data, (guint32)trans->id ); + dcop_marshal_string( data, reply_type ); + dcop_marshal_data( data, reply_data ); + + IceGetHeader( P->ice_conn, P->major_opcode, DCOPReplyDelayed, sizeof( struct DCOPMsg ), struct DCOPMsg, pMsg ); + + pMsg->key = trans->key; + pMsg->length += data->size; + + IceSendData( P->ice_conn, data->size, data->ptr ); + + dcop_data_deref( data ); + dcop_data_deref( reply_data ); + + g_free( trans->sender_id ); + g_free( trans ); +} + +void dcop_client_emit_dcop_signal( DcopClient *client, + const gchar *object, const gchar *signal, dcop_data *data ) +{ + gchar *normalized_signal_name = dcop_client_normalize_function_signature( signal ); + gchar *signame = g_strdup( object ); + + signame = (gchar *)g_realloc( signame, strlen( object ) + 1 + 1 + strlen( normalized_signal_name ) ); + strcat( signame, "#" ); + strcat( signame, normalized_signal_name ); + + dcop_client_send( client, "DCOPServer", "emit", signame, data ); + + g_free( signame ); +} + +gboolean dcop_client_connect_dcop_signal( DcopClient *client, + const gchar *sender, const gchar *sender_obj, + const gchar *signal, + const gchar *receiver_obj, const gchar *slot, + gboolean _volatile ) +{ + gchar *reply_type = 0; + dcop_data *reply_data = 0; + dcop_data *data = dcop_data_ref( dcop_data_new() ); + guint8 ivolatile = _volatile ? 1 : 0; + gchar *normalized_signame = dcop_client_normalize_function_signature( signal ); + gchar *normalized_slotname = dcop_client_normalize_function_signature( slot ); + guint8 result = 0; + + dcop_marshal_string( data, sender ); + dcop_marshal_string( data, sender_obj ); + dcop_marshal_string( data, normalized_signame ); + dcop_marshal_string( data, receiver_obj ); + dcop_marshal_string( data, normalized_slotname ); + dcop_marshal_uint8( data, ivolatile ); + + if ( dcop_client_call( client, "DCOPServer", "", "connectSignal(QCString,QCString,QCString,QCString,QCString,bool)", data, &reply_type, &reply_data ) == FALSE ) + { + g_free( normalized_signame ); + g_free( normalized_slotname ); + dcop_data_deref( data ); + return FALSE; + } + + if ( reply_type == NULL || strcmp( reply_type, "bool" ) != 0 || + reply_data == NULL ) + { + g_free( normalized_signame ); + g_free( normalized_slotname ); + dcop_data_deref( data ); + if ( reply_data != NULL ) + dcop_data_deref( reply_data ); + return FALSE; + } + + dcop_data_reset( reply_data ); + dcop_demarshal_uint8( reply_data, &result ); + + g_free( normalized_signame ); + g_free( normalized_slotname ); + dcop_data_deref( data ); + dcop_data_deref( reply_data ); + + if ( result == 0 ) + return FALSE; + + return TRUE; +} + +gboolean dcop_client_disconnect_dcop_signal( DcopClient *client, + const gchar *sender, const gchar *sender_obj, + const gchar *signal, + const gchar *receiver_obj, const gchar *slot ) +{ + gchar *reply_type = 0; + dcop_data *reply_data = 0; + dcop_data *data = dcop_data_ref( dcop_data_new() ); + gchar *normalized_signame = dcop_client_normalize_function_signature( signal ); + gchar *normalized_slotname = dcop_client_normalize_function_signature( slot ); + guint8 result = 0; + + dcop_marshal_string( data, sender ); + dcop_marshal_string( data, sender_obj ); + dcop_marshal_string( data, normalized_signame ); + dcop_marshal_string( data, receiver_obj ); + dcop_marshal_string( data, normalized_slotname ); + + if ( dcop_client_call( client, "DCOPServer", "", "disconnectSignal(QCString,QCString,QCString,QCString,QCString)", data, &reply_type, &reply_data ) == FALSE ) + { + g_free( normalized_signame ); + g_free( normalized_slotname ); + dcop_data_deref( data ); + return FALSE; + } + + if ( reply_type == NULL || strcmp( reply_type, "bool" ) != 0 || + reply_data == NULL ) + { + g_free( normalized_signame ); + g_free( normalized_slotname ); + dcop_data_deref( data ); + if ( reply_data != NULL ) + dcop_data_deref( reply_data ); + return FALSE; + } + + dcop_data_reset( reply_data ); + dcop_demarshal_uint8( reply_data, &result ); + + g_free( normalized_signame ); + g_free( normalized_slotname ); + dcop_data_deref( data ); + dcop_data_deref( reply_data ); + + if ( result == 0 ) + return FALSE; + + return TRUE; +} + +void dcop_client_set_daemon_mode( DcopClient *client, gboolean daemon ) +{ + gchar *reply_type = 0; + dcop_data *reply_data = 0; + dcop_data *data = dcop_data_ref( dcop_data_new() ); + guint8 idaemon = daemon ? 1 : 0; + dcop_marshal_uint8( data, idaemon ); + + dcop_client_call( client, "DCOPServer", "", "setDaemonMode(bool)", data, &reply_type, &reply_data ); + + if ( reply_data != NULL ) + dcop_data_deref( reply_data ); + + dcop_data_deref( data ); + + dcop_free( reply_type ); +} |