/* skype_dbus_client.c: a simple command line client to Skype's DBus interface
                        read from STDIN and write to STDOUT */

#include <stdio.h>
#include <glib.h>
#include <stdlib.h>
#include <string.h>

#define DBUS_API_SUBJECT_TO_CHANGE
#include <dbus/dbus.h>

DBusConnection *connection;

/* iterate through DBus messages and print the string components */
static void print_iter( DBusMessageIter *iter )
  {
  do
    {
    int type = dbus_message_iter_get_arg_type( iter );
    const char *str;

    if ( type == DBUS_TYPE_INVALID ) break;

    switch ( type )
      {
      case DBUS_TYPE_STRING:					// it is a string message
        dbus_message_iter_get_basic( iter, &str );
        g_print( "%s\n", str );
        break;

      case DBUS_TYPE_VARIANT:					// another set of items
        {
        DBusMessageIter subiter;
			
        dbus_message_iter_recurse( iter, &subiter );
        print_iter( &subiter );
        break;
        }

      default:
        break;
      }
    }
  while ( dbus_message_iter_next( iter ) );
  }

/* the handler gets called if the DBus connection receives any data */
static DBusHandlerResult notify_handler( DBusConnection *bus, DBusMessage *msg, void *user_data )
  {
  DBusMessageIter iter;
  dbus_message_iter_init( msg, &iter );
  print_iter( &iter );

  return TRUE;
  }

/* send a string to skype */
void sendToSkype( char *msg )
  {
  DBusMessageIter iter;
  const int reply_timeout = -1;					// do not timeout -- block
  DBusMessage *reply;
  DBusMessage *message;
  DBusError error;

  dbus_error_init( &error );
  message = dbus_message_new_method_call(
    "com.Skype.API",
    "/com/Skype",
    "com.Skype.API",
    "Invoke"
  ); 

  if( !dbus_message_append_args( message, DBUS_TYPE_STRING, &msg, DBUS_TYPE_INVALID ) )
    {
    fprintf( stderr, "Error: reply is not except format\n" );
    exit( 1 );
    }

  reply = dbus_connection_send_with_reply_and_block( connection, message, reply_timeout, &error );

  if ( dbus_error_is_set( &error ) )
    {
    fprintf ( stderr, "Error: %s\n", error.message );
    dbus_error_free( &error );
    exit( 1 );
    }

  dbus_message_iter_init( reply, &iter );
  print_iter( &iter );

  if( dbus_error_is_set( &error ) )
    {
    fprintf( stderr, "Error: %s\n", error.message );
    dbus_error_free( &error );
    exit( 1 );
    }
  }

/* if the input is disconnected: exit the program */
gboolean hangup_handler( GIOChannel *source, GIOCondition condition, gpointer data )
  { exit( 0 ); }

/* input waiting: read and forward to skype */
gboolean input_handler( GIOChannel *source, GIOCondition condition, gpointer data )
  {
  char *b;

  g_io_channel_read_line( source, &b, NULL, NULL, NULL );
  if ( b == NULL ) { exit( 0 ); }

  sendToSkype( b );
  g_free( b );

  return TRUE;
  }

int main( int argc, char **argv )
  {
  DBusObjectPathVTable vtable;
  GMainLoop *loop;
  GIOChannel *in;
  DBusError error;

  dbus_error_init( &error );
  connection = dbus_bus_get( DBUS_BUS_SESSION, &error );
  if ( connection == NULL )
    {
    fprintf( stderr, "Failed to open connection to bus: %s\n", error.message );
    dbus_error_free( &error );
    exit( 1 );
    }

  loop = g_main_loop_new( NULL, FALSE );

  in = g_io_channel_unix_new( 0 );				// open STDIN for reading
  g_io_add_watch( in, G_IO_IN, input_handler, NULL );		// watch for input on STDIN
  g_io_add_watch( in, G_IO_HUP, hangup_handler, NULL );		// watch for hangup of STDIN

  dbus_connection_setup_with_g_main( connection, NULL );	// set up te DBus connection

  vtable.message_function = notify_handler;			// register handler for incoming data on DBus
  dbus_connection_register_object_path( connection, "/com/Skype/Client", &vtable, 0 );

  g_main_loop_run( loop );					// the main loop

  return 0;
  }
