Skype DBus client program for Linux

In order to access Skype with shell scripts or Perl, I decided to use a single interface suitable for all possible uses: read/write from STDIN/STDOUT. This also allows for playing with the Skype API interactively by typing in the commands. Be careful not to enter empty lines – this may cause Skype to crash.

The program skype_dbus_client.c can be compiled with

gcc skype_dbus_client.c -o skype_dbus_client \
      `pkg-config --cflags --libs glib-2.0` \
      `pkg-config --cflags --libs dbus-glib-1`

Of course, libraries and headers of GLib and the GLib DBus bindings have to be installed.

  1. /* skype_dbus_client.c: a simple command line client to Skype's DBus interface
  2. read from STDIN and write to STDOUT */
  3.  
  4. #include <stdio.h>
  5. #include <glib.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8.  
  9. #define DBUS_API_SUBJECT_TO_CHANGE
  10. #include <dbus/dbus.h>
  11.  
  12. DBusConnection *connection;
  13.  
  14. /* iterate through DBus messages and print the string components */
  15. static void print_iter( DBusMessageIter *iter )
  16. {
  17. do
  18. {
  19. int type = dbus_message_iter_get_arg_type( iter );
  20. const char *str;
  21.  
  22. if ( type == DBUS_TYPE_INVALID ) break;
  23.  
  24. switch ( type )
  25. {
  26. case DBUS_TYPE_STRING: // it is a string message
  27. dbus_message_iter_get_basic( iter, &str );
  28. g_print( "%s\n", str );
  29. break;
  30.  
  31. case DBUS_TYPE_VARIANT: // another set of items
  32. {
  33. DBusMessageIter subiter;
  34.  
  35. dbus_message_iter_recurse( iter, &subiter );
  36. print_iter( &subiter );
  37. break;
  38. }
  39.  
  40. default:
  41. break;
  42. }
  43. }
  44. while ( dbus_message_iter_next( iter ) );
  45. }
  46.  
  47. /* the handler gets called if the DBus connection receives any data */
  48. static DBusHandlerResult notify_handler( DBusConnection *bus, DBusMessage *msg, void *user_data )
  49. {
  50. DBusMessageIter iter;
  51. dbus_message_iter_init( msg, &iter );
  52. print_iter( &iter );
  53.  
  54. return TRUE;
  55. }
  56.  
  57. /* send a string to skype */
  58. void sendToSkype( char *msg )
  59. {
  60. DBusMessageIter iter;
  61. const int reply_timeout = -1; // do not timeout -- block
  62. DBusMessage *reply;
  63. DBusMessage *message;
  64. DBusError error;
  65.  
  66. dbus_error_init( &error );
  67. message = dbus_message_new_method_call(
  68. "com.Skype.API",
  69. "/com/Skype",
  70. "com.Skype.API",
  71. "Invoke"
  72. );
  73.  
  74. if( !dbus_message_append_args( message, DBUS_TYPE_STRING, &msg, DBUS_TYPE_INVALID ) )
  75. {
  76. fprintf( stderr, "Error: reply is not except format\n" );
  77. exit( 1 );
  78. }
  79.  
  80. reply = dbus_connection_send_with_reply_and_block( connection, message, reply_timeout, &error );
  81.  
  82. if ( dbus_error_is_set( &error ) )
  83. {
  84. fprintf ( stderr, "Error: %s\n", error.message );
  85. dbus_error_free( &error );
  86. exit( 1 );
  87. }
  88.  
  89. dbus_message_iter_init( reply, &iter );
  90. print_iter( &iter );
  91.  
  92. if( dbus_error_is_set( &error ) )
  93. {
  94. fprintf( stderr, "Error: %s\n", error.message );
  95. dbus_error_free( &error );
  96. exit( 1 );
  97. }
  98. }
  99.  
  100. /* if the input is disconnected: exit the program */
  101. gboolean hangup_handler( GIOChannel *source, GIOCondition condition, gpointer data )
  102. { exit( 0 ); }
  103.  
  104. /* input waiting: read and forward to skype */
  105. gboolean input_handler( GIOChannel *source, GIOCondition condition, gpointer data )
  106. {
  107. char *b;
  108.  
  109. g_io_channel_read_line( source, &b, NULL, NULL, NULL );
  110. if ( b == NULL ) { exit( 0 ); }
  111.  
  112. sendToSkype( b );
  113. g_free( b );
  114.  
  115. return TRUE;
  116. }
  117.  
  118. int main( int argc, char **argv )
  119. {
  120. DBusObjectPathVTable vtable;
  121. GMainLoop *loop;
  122. GIOChannel *in;
  123. DBusError error;
  124.  
  125. dbus_error_init( &error );
  126. connection = dbus_bus_get( DBUS_BUS_SESSION, &error );
  127. if ( connection == NULL )
  128. {
  129. fprintf( stderr, "Failed to open connection to bus: %s\n", error.message );
  130. dbus_error_free( &error );
  131. exit( 1 );
  132. }
  133.  
  134. loop = g_main_loop_new( NULL, FALSE );
  135.  
  136. in = g_io_channel_unix_new( 0 ); // open STDIN for reading
  137. g_io_add_watch( in, G_IO_IN, input_handler, NULL ); // watch for input on STDIN
  138. g_io_add_watch( in, G_IO_HUP, hangup_handler, NULL ); // watch for hangup of STDIN
  139.  
  140. dbus_connection_setup_with_g_main( connection, NULL ); // set up te DBus connection
  141.  
  142. vtable.message_function = notify_handler; // register handler for incoming data on DBus
  143. dbus_connection_register_object_path( connection, "/com/Skype/Client", &vtable, 0 );
  144.  
  145. g_main_loop_run( loop ); // the main loop
  146.  
  147. return 0;
  148. }