gmyth/src/gmyth_socket.c
author renatofilho
Thu Nov 30 20:28:25 2006 +0000 (2006-11-30)
branchtrunk
changeset 155 1842993f4473
parent 154 30f32f34287c
child 158 517da097a261
permissions -rw-r--r--
[svn r156] code cleanup
leo_sobral@1
     1
/**
leo_sobral@1
     2
 * GMyth Library
leo_sobral@1
     3
 *
leo_sobral@1
     4
 * @file gmyth/gmyth_socket.c
leo_sobral@1
     5
 * 
leo_sobral@1
     6
 * @brief <p> MythTV socket implementation, according to the MythTV Project
leo_sobral@1
     7
 * (www.mythtv.org). 
leo_sobral@1
     8
 * 
leo_sobral@1
     9
 * This component provides basic socket functionalities to interact with
leo_sobral@1
    10
 * the Mythtv backend.
leo_sobral@1
    11
 * <p>
leo_sobral@1
    12
 *
leo_sobral@1
    13
 * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
leo_sobral@1
    14
 * @author Rosfran Lins Borges <rosfran.borges@indt.org.br> 
leo_sobral@1
    15
 *
leo_sobral@1
    16
 *//*
leo_sobral@1
    17
 * 
leo_sobral@1
    18
 * This program is free software; you can redistribute it and/or modify
leo_sobral@1
    19
 * it under the terms of the GNU Lesser General Public License as published by
leo_sobral@1
    20
 * the Free Software Foundation; either version 2 of the License, or
leo_sobral@1
    21
 * (at your option) any later version.
leo_sobral@1
    22
 *
leo_sobral@1
    23
 * This program is distributed in the hope that it will be useful,
leo_sobral@1
    24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
leo_sobral@1
    25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
leo_sobral@1
    26
 * GNU General Public License for more details.
leo_sobral@1
    27
 *
leo_sobral@1
    28
 * You should have received a copy of the GNU Lesser General Public License
leo_sobral@1
    29
 * along with this program; if not, write to the Free Software
leo_sobral@1
    30
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
leo_sobral@1
    31
 */
leo_sobral@1
    32
rosfran@105
    33
#ifdef HAVE_CONFIG_H
rosfran@105
    34
#  include "config.h"
rosfran@105
    35
#endif
rosfran@105
    36
leo_sobral@1
    37
#include <glib.h> 
leo_sobral@1
    38
#include <glib/gprintf.h>
leo_sobral@1
    39
leo_sobral@1
    40
#include <arpa/inet.h>
leo_sobral@1
    41
#include <sys/types.h>
leo_sobral@1
    42
#include <sys/socket.h>
leo_sobral@1
    43
#include <netdb.h>
rosfran@104
    44
#include <net/if.h>
leo_sobral@1
    45
#include <errno.h>
leo_sobral@1
    46
#include <stdlib.h>
leo_sobral@1
    47
rosfran@105
    48
#include <unistd.h>
rosfran@105
    49
#include <netinet/in.h>
rosfran@105
    50
#include <fcntl.h>
rosfran@105
    51
#include <signal.h>
rosfran@105
    52
rosfran@104
    53
#if defined(HAVE_IFADDRS_H)
rosfran@104
    54
	#include <ifaddrs.h>
rosfran@104
    55
#else
rosfran@104
    56
	#include <sys/ioctl.h>
rosfran@104
    57
#endif
rosfran@104
    58
leo_sobral@1
    59
#include "gmyth_socket.h"
leo_sobral@1
    60
#include "gmyth_stringlist.h"
rosfran@104
    61
#include "gmyth_uri.h"
renatofilho@131
    62
#include "gmyth_debug.h"
leo_sobral@1
    63
rosfran@101
    64
#define BUFLEN 				        			512
rosfran@101
    65
#define MYTH_SEPARATOR 			    		"[]:[]"
rosfran@101
    66
#define MYTH_PROTOCOL_FIELD_SIZE		8
rosfran@101
    67
rosfran@101
    68
/* max number of iterations */
rosfran@105
    69
#define MYTHTV_MAX_VERSION_CHECKS		40
leo_sobral@1
    70
melunko@117
    71
// FIXME: put this in the right place
melunko@117
    72
#define  MYTHTV_VERSION_DEFAULT			30
melunko@117
    73
leo_sobral@1
    74
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
leo_sobral@1
    75
leo_sobral@1
    76
static void gmyth_socket_class_init          (GMythSocketClass *klass);
leo_sobral@1
    77
static void gmyth_socket_init                (GMythSocket *object);
leo_sobral@1
    78
leo_sobral@1
    79
static void gmyth_socket_dispose  (GObject *object);
leo_sobral@1
    80
static void gmyth_socket_finalize (GObject *object);
leo_sobral@1
    81
leo_sobral@1
    82
G_DEFINE_TYPE(GMythSocket, gmyth_socket, G_TYPE_OBJECT)
leo_sobral@1
    83
leo_sobral@1
    84
static void
leo_sobral@1
    85
gmyth_socket_class_init (GMythSocketClass *klass)
leo_sobral@1
    86
{
leo_sobral@1
    87
    GObjectClass *gobject_class;
leo_sobral@1
    88
leo_sobral@1
    89
    gobject_class = (GObjectClass *) klass;
leo_sobral@1
    90
leo_sobral@1
    91
    gobject_class->dispose  = gmyth_socket_dispose;
leo_sobral@1
    92
    gobject_class->finalize = gmyth_socket_finalize;	
leo_sobral@1
    93
}
leo_sobral@1
    94
leo_sobral@1
    95
static void
leo_sobral@1
    96
gmyth_socket_init (GMythSocket *gmyth_socket)
leo_sobral@1
    97
{
leo_sobral@1
    98
}
leo_sobral@1
    99
leo_sobral@1
   100
/** Gets the some important address translation info, from the client socket
leo_sobral@1
   101
 * that will open a connection.
leo_sobral@1
   102
 * 
leo_sobral@1
   103
 * @return gint that represents the error number from getaddrinfo(). 
leo_sobral@1
   104
 */
leo_sobral@1
   105
static gint
renatofilho@147
   106
gmyth_socket_toaddrinfo (gchar *addr, gint port, struct addrinfo **addrInfo )
leo_sobral@1
   107
{
renatofilho@147
   108
    struct addrinfo *hints;
renatofilho@147
   109
    gchar *portStr = NULL;
leo_sobral@1
   110
    gint errorn = EADDRNOTAVAIL;
leo_sobral@1
   111
rosfran@154
   112
renatofilho@147
   113
    hints = g_new0 (struct addrinfo, 1);
renatofilho@147
   114
    hints->ai_family = AF_INET;
renatofilho@147
   115
    hints->ai_socktype = SOCK_STREAM;
rosfran@154
   116
    hints->ai_flags = AI_NUMERICHOST;
rosfran@154
   117
    
leo_sobral@1
   118
    if ( port != -1 )	
renatofilho@147
   119
        portStr = g_strdup_printf ("%d", port);
leo_sobral@1
   120
    else
rosfran@154
   121
				portStr = NULL;
leo_sobral@1
   122
renatofilho@131
   123
    gmyth_debug ("Address: %s, port: %s\n", addr, portStr);
renatofilho@147
   124
    if ( ( errorn = getaddrinfo(addr, portStr, hints, addrInfo) ) != 0 ) {
leo_sobral@1
   125
		g_printerr( "[%s] Socket ERROR: %s\n", __FUNCTION__, gai_strerror(errorn) );
leo_sobral@1
   126
    }
renatofilho@147
   127
    g_free (portStr);
renatofilho@147
   128
    g_free (hints);
leo_sobral@1
   129
    return errorn;
leo_sobral@1
   130
}
leo_sobral@1
   131
rosfran@104
   132
static gint
rosfran@105
   133
gmyth_socket_find_match_address_uri( GMythURI* uri, gchar *address ) {
rosfran@104
   134
rosfran@121
   135
        if ( g_ascii_strcasecmp( gmyth_uri_get_host( uri ), address ) == 0 ) {
rosfran@104
   136
                //g_printerr( "Found URI: %s !!!\n", rui_uri_getvalue(uri) );
rosfran@104
   137
                return 0;
rosfran@104
   138
        } else {
rosfran@104
   139
                return -1;
rosfran@104
   140
        }
rosfran@104
   141
rosfran@104
   142
}
rosfran@104
   143
rosfran@104
   144
#if defined(HAVE_IFADDRS_H)
rosfran@104
   145
rosfran@105
   146
/** Gets the list of all local network interfaces.
rosfran@105
   147
 * 
rosfran@105
   148
 * @param current_connections	A list with all the network interfaces are valid, 
rosfran@105
   149
 * 		to be applied just like a filter.
rosfran@105
   150
 * @return List with all the local net interfaces. 
rosfran@105
   151
 */
rosfran@105
   152
static GList *
rosfran@105
   153
gmyth_socket_get_local_addrs( GList *current_connections ) {
rosfran@104
   154
	
rosfran@104
   155
	GList *local_addrs = NULL;
rosfran@104
   156
	
rosfran@115
   157
	struct ifaddrs *ifaddr = g_new0( struct ifaddrs, 1 );	
rosfran@115
   158
	struct ifaddrs *i = g_new0( struct ifaddrs, 1 );
rosfran@104
   159
rosfran@104
   160
	gchar addr[NI_MAXHOST+1];
rosfran@104
   161
	gchar *ifname;
rosfran@104
   162
	gint ifIdx;
rosfran@104
   163
	
rosfran@104
   164
	if (getifaddrs(&ifaddr) != 0)
rosfran@104
   165
	{
rosfran@104
   166
		g_printerr("No addresses for interfaces!\n");
rosfran@104
   167
		return NULL;
rosfran@104
   168
	}
rosfran@104
   169
	
rosfran@104
   170
	for ( i = ifaddr; i != NULL; i = i->ifa_next ) {
rosfran@104
   171
		if (!(i->ifa_flags & IFF_UP))
rosfran@104
   172
			continue;
rosfran@104
   173
		if (i->ifa_flags & IFF_LOOPBACK)
rosfran@104
   174
			continue;
rosfran@104
   175
		if ( getnameinfo(i->ifa_addr, sizeof(struct sockaddr), addr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) == 0 ) {
rosfran@104
   176
rosfran@104
   177
			ifname = i->ifa_name;
rosfran@104
   178
			ifIdx = if_nametoindex(ifname);
renatofilho@131
   179
			/* gmyth_debug( "[%s] Interface name: %s, index: %d, address: %s\n", __FUNCTION__, ifname, ifIdx, addr ); */
rosfran@104
   180
			if ( current_connections == NULL || ( current_connections != NULL && 
rosfran@104
   181
					g_list_find_custom( current_connections, (gchar *)addr, 
rosfran@105
   182
						(GCompareFunc)gmyth_socket_find_match_address_uri ) == NULL ) )
renatofilho@155
   183
				local_addrs = g_list_append (local_addrs, addr);
rosfran@104
   184
rosfran@104
   185
		}
rosfran@105
   186
	} /* iterates over network interfaces */
rosfran@104
   187
	
rosfran@104
   188
	freeifaddrs(ifaddr);
rosfran@104
   189
	
rosfran@104
   190
	return local_addrs;
rosfran@104
   191
rosfran@104
   192
}
rosfran@104
   193
rosfran@104
   194
#else
rosfran@104
   195
rosfran@154
   196
static const gchar *PATH_PROC_NET_DEV = "/proc/net/dev";
rosfran@104
   197
rosfran@105
   198
/** Gets the list of all local network interfaces (using the /proc/net/dev directory).
rosfran@105
   199
 * 
rosfran@105
   200
 * @param current_connections	A list with all the network interfaces are valid, 
rosfran@105
   201
 * 		to be applied just like a filter.
rosfran@105
   202
 * @return List with all the local net interfaces. 
rosfran@105
   203
 */
rosfran@105
   204
static GList *
rosfran@105
   205
gmyth_socket_get_local_addrs( GList *current_connections )
rosfran@104
   206
{
rosfran@104
   207
rosfran@104
   208
	GList *local_addrs = NULL;
rosfran@104
   209
	FILE *fd;
rosfran@104
   210
	gint s;
rosfran@104
   211
	gchar buffer[256+1];
rosfran@104
   212
	gchar ifaddr[20+1];
rosfran@104
   213
	gchar *ifname;
rosfran@104
   214
	gchar *sep;
rosfran@104
   215
	
rosfran@104
   216
	s = socket(AF_INET, SOCK_DGRAM, 0);
rosfran@104
   217
	if (s < 0)
rosfran@104
   218
		return 0;
rosfran@104
   219
	fd = fopen(PATH_PROC_NET_DEV, "r");
rosfran@104
   220
	fgets(buffer, sizeof(buffer)-1, fd);
rosfran@104
   221
	fgets(buffer, sizeof(buffer)-1, fd);
rosfran@104
   222
	while (!feof(fd)) {
rosfran@104
   223
		ifname = buffer;
rosfran@104
   224
		sep;
rosfran@104
   225
		if (fgets(buffer, sizeof(buffer)-1, fd) == NULL)
rosfran@104
   226
			break;
rosfran@104
   227
		sep = strrchr(buffer, ':');
rosfran@104
   228
		if (sep)
rosfran@104
   229
			*sep = 0;
rosfran@104
   230
		while (*ifname == ' ')
rosfran@104
   231
			ifname++;
rosfran@104
   232
		struct ifreq req;
rosfran@104
   233
		strcpy(req.ifr_name, ifname);
rosfran@104
   234
		if (ioctl(s, SIOCGIFFLAGS, &req) < 0)
rosfran@104
   235
			continue;
rosfran@104
   236
		if (!(req.ifr_flags & IFF_UP))
rosfran@104
   237
			continue;
rosfran@104
   238
		if (req.ifr_flags & IFF_LOOPBACK)
rosfran@104
   239
			continue;
rosfran@104
   240
		if (ioctl(s, SIOCGIFADDR, &req) < 0)
rosfran@104
   241
			continue;
rosfran@104
   242
		g_strlcpy( ifaddr, inet_ntoa(((struct sockaddr_in*)&req.ifr_addr)->sin_addr), sizeof(struct ifaddr)-1 );
rosfran@104
   243
		local_addrs = g_list_append( local_addrs, g_strdup( ifaddr ) );
rosfran@104
   244
renatofilho@131
   245
		gmyth_debug( "[%s] ( from the /proc/net/dev) Interface name: %s, address: %s\n", __FUNCTION__, 
rosfran@104
   246
								ifname, ifaddr );
rosfran@104
   247
	}
rosfran@104
   248
	fclose(fd);
rosfran@104
   249
	close(s);
rosfran@104
   250
	return local_addrs;
rosfran@104
   251
rosfran@104
   252
}
rosfran@104
   253
rosfran@104
   254
#endif
rosfran@104
   255
rosfran@104
   256
/**
rosfran@104
   257
 * Get only the local addresses from the primary interface
rosfran@104
   258
 */
rosfran@105
   259
static gchar *
rosfran@105
   260
gmyth_socket_get_primary_addr()
rosfran@104
   261
{
rosfran@104
   262
	
rosfran@104
   263
	gchar *if_eth0 = g_new0( gchar, sizeof(struct ifaddr)-1 );
rosfran@104
   264
	GList *if_tmp = NULL;
rosfran@104
   265
	
rosfran@105
   266
	GList *interfs = gmyth_socket_get_local_addrs( NULL );
rosfran@104
   267
	
rosfran@104
   268
	if ( interfs != NULL && ( g_list_length( interfs ) > 0 ) ) 
rosfran@104
   269
	{
rosfran@104
   270
		/* get the first occurrence (primary interface) */
rosfran@104
   271
		if_tmp = g_list_first( interfs );
rosfran@104
   272
		
rosfran@104
   273
		if ( if_tmp != NULL )
renatofilho@155
   274
			g_strlcpy (if_eth0, (gchar *)if_tmp->data, sizeof(struct ifaddr)-1 );
rosfran@104
   275
rosfran@104
   276
	}
rosfran@104
   277
	
rosfran@104
   278
	if ( interfs != NULL )
rosfran@104
   279
		g_list_free( interfs );
rosfran@104
   280
	
rosfran@104
   281
	return if_eth0;
rosfran@104
   282
}
rosfran@104
   283
leo_sobral@1
   284
/** This function retrieves the local hostname of the 
leo_sobral@1
   285
 * client machine.
leo_sobral@1
   286
 *
leo_sobral@1
   287
 * @return GString* get local hostname.
leo_sobral@1
   288
 */
leo_sobral@1
   289
GString *
renatofilho@147
   290
gmyth_socket_get_local_hostname  ()
leo_sobral@1
   291
{
renatofilho@155
   292
    GString *str = NULL;
leo_sobral@1
   293
renatofilho@147
   294
    gchar localhostname[1024];
renatofilho@147
   295
    gchar *localaddr = NULL; 
leo_sobral@1
   296
    gboolean found_addr = FALSE;
leo_sobral@1
   297
    struct addrinfo* addr_info_data = NULL, *addr_info0 = NULL;
leo_sobral@1
   298
    struct sockaddr_in* sa = NULL;
leo_sobral@1
   299
renatofilho@155
   300
    gethostname (localhostname, 1024);
renatofilho@147
   301
    gint err = gmyth_socket_toaddrinfo (localhostname, -1,  &addr_info_data );
rosfran@105
   302
    
rosfran@105
   303
    if ( err == EADDRNOTAVAIL )
rosfran@105
   304
    {
rosfran@105
   305
    	g_warning( "[%s] Address (%s) not available. (reason = %d)\n", __FUNCTION__, localhostname, err );
rosfran@105
   306
    	return str;
rosfran@105
   307
    }
rosfran@105
   308
    
rosfran@105
   309
    g_static_mutex_lock( &mutex );    	
leo_sobral@1
   310
leo_sobral@1
   311
    addr_info0 = addr_info_data;
leo_sobral@1
   312
leo_sobral@1
   313
    while( addr_info0 != NULL && addr_info0->ai_addr != NULL && 
leo_sobral@1
   314
	    ( sa = (struct sockaddr_in*)addr_info0->ai_addr ) != NULL && !found_addr ) {
leo_sobral@1
   315
    	localaddr = inet_ntoa( sa->sin_addr );
leo_sobral@1
   316
leo_sobral@1
   317
	    if ( localaddr != NULL && ( g_strrstr( localaddr, "127" ) == NULL ) ) {
renatofilho@155
   318
	        str = g_string_new (localaddr);
leo_sobral@1
   319
	        found_addr = TRUE;
leo_sobral@1
   320
	        break;
leo_sobral@1
   321
	    }
leo_sobral@1
   322
	    addr_info0 = addr_info0->ai_next;
leo_sobral@1
   323
    };
renatofilho@147
   324
    freeaddrinfo (addr_info_data);
leo_sobral@1
   325
leo_sobral@1
   326
    if ( found_addr == FALSE ) {
renatofilho@155
   327
        gchar *prim_addr = gmyth_socket_get_primary_addr();
rosfran@105
   328
rosfran@105
   329
    	if ( prim_addr != NULL ) {
renatofilho@155
   330
		    g_warning("[%s] Could not determine the local alphanumerical hostname. Setting to %s\n",
renatofilho@155
   331
    	        __FUNCTION__, prim_addr );
rosfran@104
   332
      
renatofilho@155
   333
	        str = g_string_new (prim_addr);
renatofilho@155
   334
	        g_free (prim_addr);
rosfran@104
   335
    	} else {
renatofilho@155
   336
        	str = g_string_new (localhostname);
rosfran@104
   337
    	}
leo_sobral@1
   338
    }
leo_sobral@1
   339
renatofilho@155
   340
    g_static_mutex_unlock (&mutex);
leo_sobral@1
   341
leo_sobral@1
   342
    return str;
leo_sobral@1
   343
}
leo_sobral@1
   344
leo_sobral@1
   345
static void
leo_sobral@1
   346
gmyth_socket_dispose  (GObject *object)
leo_sobral@1
   347
{
leo_sobral@1
   348
    GMythSocket *gmyth_socket = GMYTH_SOCKET(object);
leo_sobral@1
   349
renatofilho@147
   350
    /* disconnect socket */
leo_sobral@1
   351
    gmyth_socket_close_connection (gmyth_socket);
renatofilho@147
   352
renatofilho@147
   353
    g_free (gmyth_socket->hostname);
renatofilho@147
   354
    gmyth_socket->hostname = NULL;
renatofilho@147
   355
leo_sobral@1
   356
    G_OBJECT_CLASS (gmyth_socket_parent_class)->dispose (object);
leo_sobral@1
   357
}
leo_sobral@1
   358
leo_sobral@1
   359
static void
leo_sobral@1
   360
gmyth_socket_finalize (GObject *object)
leo_sobral@1
   361
{
leo_sobral@1
   362
    g_signal_handlers_destroy (object);
leo_sobral@1
   363
leo_sobral@1
   364
    G_OBJECT_CLASS (gmyth_socket_parent_class)->finalize (object);
leo_sobral@1
   365
}
leo_sobral@1
   366
leo_sobral@1
   367
/** Creates a new instance of GMythSocket.
leo_sobral@1
   368
 * 
leo_sobral@1
   369
 * @return a new instance of GMythSocket.
leo_sobral@1
   370
 */
leo_sobral@1
   371
GMythSocket*
leo_sobral@1
   372
gmyth_socket_new ()
leo_sobral@1
   373
{
leo_sobral@1
   374
    GMythSocket *gmyth_socket = GMYTH_SOCKET (g_object_new(GMYTH_SOCKET_TYPE, NULL));
leo_sobral@1
   375
leo_sobral@1
   376
    gmyth_socket->port = 6543;
rosfran@101
   377
    
rosfran@101
   378
    gmyth_socket->mythtv_version = MYTHTV_VERSION_DEFAULT;
leo_sobral@1
   379
leo_sobral@1
   380
    return gmyth_socket;
leo_sobral@1
   381
}
leo_sobral@1
   382
rosfran@105
   383
/** Try to open an asynchronous connection to the MythTV backend.
rosfran@105
   384
 * 
rosfran@105
   385
 * @param fd 			Socket descriptor.
rosfran@105
   386
 * @param remote 	Remote address.
rosfran@105
   387
 * @param len 		Newly created socket length field.
rosfran@105
   388
 * @param timeout	Timeval argument with the time interval to timeout before closing.
rosfran@105
   389
 * @param err			Error message number.
rosfran@105
   390
 * @return Any numerical value below 0, if an error had been found.
rosfran@105
   391
 */
rosfran@105
   392
static gint 
rosfran@105
   393
gmyth_socket_try_connect ( gint fd, struct sockaddr *remote, gint len,
rosfran@105
   394
               struct timeval *timeout, gint *err)
rosfran@105
   395
{
rosfran@105
   396
	  gint saveflags, ret, back_err;
rosfran@105
   397
	  
rosfran@105
   398
	  fd_set fd_w;
rosfran@105
   399
	
rosfran@105
   400
	  saveflags = fcntl( fd, F_GETFL, 0 );
rosfran@105
   401
	  if( saveflags < 0 ) {
rosfran@105
   402
	    g_warning( "[%s] Problems when getting socket flags on fcntl.\n", __FUNCTION__ );
rosfran@105
   403
	    *err=errno;
rosfran@105
   404
	    return -1;
rosfran@105
   405
	  }
rosfran@105
   406
	
rosfran@105
   407
	  /* Set non blocking */
rosfran@105
   408
	  if( fcntl( fd, F_SETFL, saveflags | O_NONBLOCK ) < 0) {
rosfran@105
   409
	  	g_warning( "[%s] Problems when setting non-blocking using fcntl.\n", __FUNCTION__ );
rosfran@105
   410
	    *err=errno;
rosfran@105
   411
	    return -1;
rosfran@105
   412
	  }
rosfran@105
   413
	
rosfran@105
   414
	  /* This will return immediately */
rosfran@105
   415
	  *err= connect ( fd, remote, len );
rosfran@105
   416
		back_err=errno;
rosfran@105
   417
	
rosfran@105
   418
		/* restore flags */
rosfran@105
   419
	  if( fcntl( fd, F_SETFL, saveflags ) < 0) {
rosfran@105
   420
	    g_warning( "[%s] Problems when trying to restore flags with fcntl.\n", __FUNCTION__ );
rosfran@105
   421
	    *err=errno;
rosfran@105
   422
	    return -1;
rosfran@105
   423
	  }
rosfran@105
   424
	
rosfran@105
   425
	  /* return unless the connection was successful or the connect is
rosfran@105
   426
	     still in progress. */
rosfran@105
   427
	  if( *err < 0 && back_err != EINPROGRESS) {
rosfran@105
   428
	    g_warning( "[%s] Connection unsucessfully (it is not in progress).\n", __FUNCTION__ );
rosfran@105
   429
	    *err = errno;
rosfran@105
   430
	    return -1;
rosfran@105
   431
	  }
rosfran@105
   432
	
rosfran@105
   433
	  FD_ZERO( &fd_w );
rosfran@105
   434
	  FD_SET( fd, &fd_w );
rosfran@105
   435
	
rosfran@105
   436
	  *err = select( FD_SETSIZE, NULL, &fd_w, NULL, timeout);
rosfran@105
   437
	  if ( *err < 0 ) {
rosfran@105
   438
	    g_warning( "[%s] Connection unsucessfull (timed out).\n", __FUNCTION__ );
rosfran@105
   439
	    *err=errno;
rosfran@105
   440
	    return -1;
rosfran@105
   441
	  }
rosfran@105
   442
	
rosfran@105
   443
	  /* 0 means it timeout out & no fds changed */
rosfran@105
   444
	  if(*err==0) {
rosfran@105
   445
	    close(fd);
rosfran@105
   446
	    *err=ETIMEDOUT;
rosfran@105
   447
	    return -1;
rosfran@105
   448
	  }
rosfran@105
   449
	
rosfran@105
   450
	  /* Get the return code from the connect */
rosfran@105
   451
	  len = sizeof( ret );
rosfran@105
   452
	  *err=getsockopt( fd, SOL_SOCKET, SO_ERROR, &ret, &len );
rosfran@105
   453
	  
rosfran@105
   454
	  if( *err < 0 ) {
rosfran@105
   455
	    g_warning( "[%s] Connection usnsucessfull.\n", __FUNCTION__ );
rosfran@105
   456
	    *err=errno;
rosfran@105
   457
	    return -1;
rosfran@105
   458
	  }
rosfran@105
   459
	
rosfran@105
   460
	  /* ret=0 means success, otherwise it contains the errno */
rosfran@105
   461
	  if (ret) {
rosfran@105
   462
	    *err=ret;
rosfran@105
   463
	    return -1;
rosfran@105
   464
	  }
rosfran@105
   465
	
rosfran@105
   466
	  *err=0;
rosfran@105
   467
	  return 0;
rosfran@105
   468
}
rosfran@105
   469
leo_sobral@1
   470
/** Connects to the backend.
leo_sobral@1
   471
 * 
leo_sobral@1
   472
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   473
 * @param hostname The backend hostname or IP address.
leo_sobral@1
   474
 * @param port The backend port.
leo_sobral@1
   475
 * @return TRUE if success, FALSE if error.
leo_sobral@1
   476
 */
leo_sobral@1
   477
gboolean
melunko@112
   478
gmyth_socket_connect (GMythSocket *gmyth_socket,
leo_sobral@1
   479
	gchar *hostname, gint port)
leo_sobral@1
   480
{
leo_sobral@1
   481
    struct addrinfo *addr_info_data = NULL, *addr_info0 = NULL;
leo_sobral@1
   482
    gint ret_code = -1;
leo_sobral@1
   483
    gint errno;
leo_sobral@1
   484
    gboolean ret = TRUE;
leo_sobral@1
   485
leo_sobral@1
   486
    if ( hostname == NULL )
leo_sobral@1
   487
		g_printerr ( "[%s] Invalid hostname parameter!\n", __FUNCTION__ );
leo_sobral@1
   488
leo_sobral@1
   489
    errno = gmyth_socket_toaddrinfo( hostname, port, &addr_info_data );
leo_sobral@1
   490
leo_sobral@1
   491
    g_return_val_if_fail( addr_info_data != NULL, FALSE );
leo_sobral@1
   492
leo_sobral@1
   493
    /* store hostname and port number */
melunko@112
   494
    gmyth_socket->hostname = g_strdup( hostname );
melunko@112
   495
    gmyth_socket->port = port;
leo_sobral@1
   496
leo_sobral@1
   497
    for ( addr_info0 = addr_info_data; addr_info0; addr_info0 = addr_info_data->ai_next ) {
renatofilho@147
   498
        struct sockaddr_in *sa = (struct sockaddr_in*)addr_info0->ai_addr;
renatofilho@147
   499
        /* init socket descriptor */
renatofilho@147
   500
        gmyth_socket->sd = socket( addr_info0->ai_family, addr_info0->ai_socktype,
renatofilho@147
   501
                         addr_info0->ai_protocol );
leo_sobral@1
   502
renatofilho@147
   503
        if ( gmyth_socket->sd < 0 )
renatofilho@147
   504
            continue;
leo_sobral@1
   505
renatofilho@147
   506
        gmyth_debug( "[%s] hostname = %s, sock_fd = %d, addr = %s, addr_len = %d, "\
renatofilho@147
   507
            "ai_family = %d, ai_protocol = %d\n",
renatofilho@147
   508
            __FUNCTION__, hostname, gmyth_socket->sd, inet_ntoa( sa->sin_addr ),
renatofilho@147
   509
            addr_info0->ai_addrlen, addr_info0->ai_family, addr_info0->ai_protocol );
renatofilho@147
   510
            
renatofilho@147
   511
        struct timeval *timeout = g_new0( struct timeval, 1 );
renatofilho@147
   512
        
renatofilho@147
   513
        /* using 40 seconds timeout */
renatofilho@147
   514
        timeout->tv_sec = 40;
renatofilho@147
   515
        timeout->tv_usec = 0;
renatofilho@147
   516
            
renatofilho@147
   517
        if ( gmyth_socket_try_connect( gmyth_socket->sd, (struct sockaddr *)addr_info0->ai_addr,
renatofilho@147
   518
                addr_info0->ai_addrlen, timeout, &ret_code ) < 0 )
renatofilho@147
   519
        {
renatofilho@147
   520
            g_printerr( "[%s] Error connecting to backend!\n", __FUNCTION__ );
renatofilho@147
   521
            if ( ret_code == ETIMEDOUT )
renatofilho@147
   522
            g_printerr( "[%s]\tBackend host unreachable!\n", __FUNCTION__ );
leo_sobral@1
   523
renatofilho@147
   524
            g_printerr( "ERROR: %s\n", gai_strerror(ret_code) );
renatofilho@147
   525
            continue;
renatofilho@147
   526
        }
leo_sobral@1
   527
renatofilho@147
   528
        /* only will be reached if none of the error above occurred */
renatofilho@147
   529
        break;
leo_sobral@1
   530
    }
renatofilho@147
   531
    freeaddrinfo (addr_info_data);
renatofilho@147
   532
    addr_info_data = NULL;
leo_sobral@1
   533
melunko@112
   534
    gmyth_socket->sd_io_ch = g_io_channel_unix_new( gmyth_socket->sd );
rosfran@121
   535
    
rosfran@121
   536
    GIOFlags flags = g_io_channel_get_flags (gmyth_socket->sd_io_ch);
rosfran@121
   537
		/* unset the nonblock flag */
rosfran@121
   538
		flags &= ~G_IO_FLAG_NONBLOCK;
rosfran@121
   539
		/* unset the nonblocking stuff for some time, because GNUTLS doesn't like
rosfran@121
   540
		 * that */
rosfran@121
   541
		g_io_channel_set_flags (gmyth_socket->sd_io_ch, flags, NULL);
leo_sobral@1
   542
leo_sobral@1
   543
    ret = ( ret_code == 0 ) ? TRUE : FALSE ;
leo_sobral@1
   544
leo_sobral@1
   545
    return ret;
leo_sobral@1
   546
leo_sobral@1
   547
}
leo_sobral@1
   548
leo_sobral@1
   549
/** Gets the GIOChannel associated to the given GMythSocket.
leo_sobral@1
   550
 * 
leo_sobral@1
   551
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   552
 */
leo_sobral@1
   553
GIOChannel *
leo_sobral@1
   554
gmyth_socket_get_io_channel( GMythSocket *gmyth_socket )
leo_sobral@1
   555
{
leo_sobral@1
   556
    g_return_val_if_fail( gmyth_socket != NULL, NULL );
leo_sobral@1
   557
leo_sobral@1
   558
    return gmyth_socket->sd_io_ch;
leo_sobral@1
   559
}
leo_sobral@1
   560
leo_sobral@1
   561
/** Verifies if the socket is able to read.
leo_sobral@1
   562
 * 
leo_sobral@1
   563
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   564
 * @return TRUE if the socket is able to read, FALSE if not.
leo_sobral@1
   565
 */
leo_sobral@1
   566
gboolean
leo_sobral@1
   567
gmyth_socket_is_able_to_read( GMythSocket *gmyth_socket )
leo_sobral@1
   568
{
leo_sobral@1
   569
    gboolean ret = TRUE;
leo_sobral@1
   570
leo_sobral@1
   571
    /* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   572
    GIOCondition io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
leo_sobral@1
   573
leo_sobral@1
   574
    if ( ( io_cond & G_IO_IN ) == 0 ) {
leo_sobral@1
   575
	g_warning ("[%s] IO channel is not able to send data!\n", __FUNCTION__);
leo_sobral@1
   576
	ret = FALSE;
leo_sobral@1
   577
    }
leo_sobral@1
   578
leo_sobral@1
   579
    return ret;
leo_sobral@1
   580
leo_sobral@1
   581
}
leo_sobral@1
   582
leo_sobral@1
   583
/** Verifies if the socket is able to write.
leo_sobral@1
   584
 * 
leo_sobral@1
   585
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   586
 * @return TRUE if the socket is able to write, FALSE if not.
leo_sobral@1
   587
 */
leo_sobral@1
   588
gboolean
leo_sobral@1
   589
gmyth_socket_is_able_to_write( GMythSocket *gmyth_socket )
leo_sobral@1
   590
{
leo_sobral@1
   591
    gboolean ret = TRUE;
leo_sobral@1
   592
leo_sobral@1
   593
    /* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   594
    GIOCondition io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
leo_sobral@1
   595
leo_sobral@1
   596
    if ( ( ( io_cond & G_IO_OUT ) == 0 ) || ( ( io_cond & G_IO_HUP ) == 0 ) ) {
leo_sobral@1
   597
	g_warning ("[%s] IO channel is not able to send data!\n", __FUNCTION__);
leo_sobral@1
   598
	ret = FALSE;
leo_sobral@1
   599
    }
leo_sobral@1
   600
leo_sobral@1
   601
    return ret;
leo_sobral@1
   602
leo_sobral@1
   603
}
leo_sobral@1
   604
leo_sobral@1
   605
/** Sends a command to the backend.
leo_sobral@1
   606
 * 
leo_sobral@1
   607
 * @param gmyth_socket the GMythSocket instance.
leo_sobral@1
   608
 * @param command The string command to be sent.
leo_sobral@1
   609
 */
leo_sobral@1
   610
gboolean
rosfran@101
   611
gmyth_socket_send_command(GMythSocket *gmyth_socket, GString *command) 
leo_sobral@1
   612
{
leo_sobral@1
   613
    gboolean ret = TRUE;
leo_sobral@1
   614
leo_sobral@1
   615
    GIOStatus io_status = G_IO_STATUS_NORMAL;
leo_sobral@1
   616
    //GIOCondition io_cond;
leo_sobral@1
   617
    GError* error = NULL;
leo_sobral@1
   618
    gchar *buffer = NULL;
leo_sobral@1
   619
leo_sobral@1
   620
    gsize bytes_written = 0;
leo_sobral@1
   621
rosfran@29
   622
    if( command == NULL || ( command->len <= 0 ) || command->str == NULL ) {
leo_sobral@1
   623
		g_warning ("[%s] Invalid NULL command parameter!\n", __FUNCTION__);
leo_sobral@1
   624
		ret = FALSE;
leo_sobral@1
   625
		goto done;
leo_sobral@1
   626
    }
leo_sobral@1
   627
leo_sobral@1
   628
    g_static_mutex_lock( &mutex );
renatofilho@131
   629
    gmyth_debug ("[%s] Sending command to backend: %s\n", __FUNCTION__, command->str);
leo_sobral@1
   630
leo_sobral@1
   631
    buffer = g_strnfill( BUFLEN, ' ' );
rosfran@154
   632
    g_snprintf( buffer, MYTH_PROTOCOL_FIELD_SIZE+1, "%-8d", command->len);
renatofilho@131
   633
    gmyth_debug ( "[%s] buffer = [%s]\n", __FUNCTION__, buffer  );
leo_sobral@1
   634
leo_sobral@1
   635
    command = g_string_prepend(command, buffer);
leo_sobral@1
   636
renatofilho@131
   637
    gmyth_debug ( "[%s] command = [%s]\n", __FUNCTION__, command->str  );
leo_sobral@1
   638
leo_sobral@1
   639
    /* write bytes to socket */    
leo_sobral@1
   640
    io_status = g_io_channel_write_chars( gmyth_socket->sd_io_ch, command->str, 
leo_sobral@1
   641
	    command->len, &bytes_written, &error );
leo_sobral@1
   642
leo_sobral@1
   643
leo_sobral@1
   644
    if( (io_status == G_IO_STATUS_ERROR) || ( bytes_written <= 0 ) ) {
leo_sobral@1
   645
		g_warning ("[%s] Error while writing to socket", __FUNCTION__);
leo_sobral@1
   646
		ret = FALSE;
leo_sobral@1
   647
    } else if ( bytes_written < command->len ) {
leo_sobral@1
   648
		g_warning ("[%s] Not all data was written socket", __FUNCTION__);
leo_sobral@1
   649
		ret = FALSE;
leo_sobral@1
   650
    }
leo_sobral@1
   651
leo_sobral@1
   652
    io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error );
leo_sobral@1
   653
leo_sobral@1
   654
    if ( ( bytes_written != command->len ) || ( io_status == G_IO_STATUS_ERROR ) )
leo_sobral@1
   655
    {
leo_sobral@1
   656
		g_warning ("[%s] Some problem occurred when sending data to the socket\n", __FUNCTION__);
leo_sobral@1
   657
	
leo_sobral@1
   658
		ret = TRUE;
leo_sobral@1
   659
    }
leo_sobral@1
   660
leo_sobral@1
   661
    g_static_mutex_unlock( &mutex );
leo_sobral@1
   662
done:
leo_sobral@1
   663
    if ( error != NULL ) {
leo_sobral@1
   664
		g_printerr( "[%s] Error found reading data from IO channel: (%d, %s)\n", __FUNCTION__, error->code, error->message );
leo_sobral@1
   665
		ret = FALSE;
leo_sobral@1
   666
		g_error_free( error );
leo_sobral@1
   667
    }
leo_sobral@1
   668
leo_sobral@1
   669
    if ( buffer!= NULL )
leo_sobral@1
   670
		g_free( buffer );
leo_sobral@1
   671
leo_sobral@1
   672
    return ret;
leo_sobral@1
   673
}
leo_sobral@1
   674
leo_sobral@1
   675
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
leo_sobral@1
   676
 * supported by the backend and send the "ANN" command.
leo_sobral@1
   677
 * 
leo_sobral@1
   678
 * @param gmyth_socket the GMythSocket instance.
leo_sobral@1
   679
 * @param hostname_backend The backend hostname or IP address.
leo_sobral@1
   680
 * @param port The backend port to connect.
leo_sobral@1
   681
 * @param blocking_client A flag to choose between blocking and non-blocking 
leo_sobral@1
   682
 * backend connection.
leo_sobral@1
   683
 */
leo_sobral@1
   684
gboolean
leo_sobral@1
   685
gmyth_socket_connect_to_backend (GMythSocket *gmyth_socket, 
leo_sobral@1
   686
	gchar *hostname_backend, int port, gboolean blocking_client)
leo_sobral@1
   687
{
melunko@112
   688
    if (!gmyth_socket_connect (gmyth_socket, hostname_backend, port)) {
rosfran@123
   689
		g_warning ("[%s] Could not open socket to backend machine [%s]\n", __FUNCTION__,
rosfran@123
   690
					hostname_backend );
leo_sobral@1
   691
		return FALSE;
leo_sobral@1
   692
    }
leo_sobral@1
   693
rosfran@101
   694
    if ( gmyth_socket_check_protocol_version (gmyth_socket) ) {
leo_sobral@1
   695
leo_sobral@1
   696
	GString *result;
leo_sobral@1
   697
	GString *base_str = g_string_new("");
leo_sobral@1
   698
	GString *hostname = NULL;
leo_sobral@1
   699
leo_sobral@1
   700
	hostname = gmyth_socket_get_local_hostname();
leo_sobral@1
   701
leo_sobral@1
   702
	g_string_printf(base_str, "ANN %s %s 0", 
leo_sobral@1
   703
		(blocking_client ? "Playback" : "Monitor"),
leo_sobral@1
   704
		hostname->str);
leo_sobral@1
   705
renatofilho@131
   706
	gmyth_debug ("[%s] Connection command sent to backend: %s", __FUNCTION__, base_str->str);
leo_sobral@1
   707
leo_sobral@1
   708
	gmyth_socket_send_command (gmyth_socket, base_str);
leo_sobral@1
   709
	result = gmyth_socket_receive_response(gmyth_socket);
leo_sobral@1
   710
leo_sobral@1
   711
	if (result != NULL) {
renatofilho@131
   712
	    gmyth_debug ("[%s] Response received from backend: %s", __FUNCTION__, result->str);
leo_sobral@1
   713
	    g_string_free (result, TRUE);
leo_sobral@1
   714
	}
leo_sobral@1
   715
leo_sobral@1
   716
	g_string_free (hostname, TRUE);
leo_sobral@1
   717
	g_string_free (base_str, TRUE);
leo_sobral@1
   718
leo_sobral@1
   719
	return TRUE;
leo_sobral@1
   720
    } else {
melunko@112
   721
	g_warning ("[%s] GMythSocket could not connect to the backend", __FUNCTION__);	
melunko@112
   722
	return FALSE;
melunko@112
   723
    }
leo_sobral@1
   724
}
leo_sobral@1
   725
leo_sobral@1
   726
/** Closes the socket connection to the backend.
leo_sobral@1
   727
 * 
leo_sobral@1
   728
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   729
 */
leo_sobral@1
   730
void
leo_sobral@1
   731
gmyth_socket_close_connection (GMythSocket *gmyth_socket)
leo_sobral@1
   732
{
leo_sobral@1
   733
    close (gmyth_socket->sd);	
leo_sobral@1
   734
}
leo_sobral@1
   735
leo_sobral@1
   736
leo_sobral@1
   737
/** Try the MythTV version numbers, and get the version returned by
leo_sobral@1
   738
 * the possible REJECT message, in order to contruct a new
leo_sobral@1
   739
 * MythTV version request.
leo_sobral@1
   740
 * 
leo_sobral@1
   741
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   742
 * @param mythtv_version The Mythtv protocol version to be tested
rosfran@101
   743
 * 
rosfran@101
   744
 * @return The actual MythTV the client is connected to.
leo_sobral@1
   745
 */
rosfran@101
   746
gint
leo_sobral@1
   747
gmyth_socket_check_protocol_version_number (GMythSocket *gmyth_socket, gint mythtv_version)
leo_sobral@1
   748
{
renatofilho@147
   749
    GString *response = NULL;
renatofilho@147
   750
    GString *payload = NULL;
leo_sobral@1
   751
    gboolean res = TRUE;
rosfran@101
   752
    gint mythtv_new_version = MYTHTV_CANNOT_NEGOTIATE_VERSION;
rosfran@101
   753
    guint max_iterations = MYTHTV_MAX_VERSION_CHECKS;
leo_sobral@1
   754
leo_sobral@1
   755
try_new_version:
leo_sobral@1
   756
    payload = g_string_new ("MYTH_PROTO_VERSION");
leo_sobral@1
   757
    g_string_append_printf( payload, " %d", mythtv_version );
leo_sobral@1
   758
leo_sobral@1
   759
    gmyth_socket_send_command(gmyth_socket, payload);
leo_sobral@1
   760
    response = gmyth_socket_receive_response(gmyth_socket);
leo_sobral@1
   761
leo_sobral@1
   762
    if (response == NULL) {
leo_sobral@1
   763
		g_warning ("[%s] Check protocol version error! Not answered!", __FUNCTION__);
leo_sobral@1
   764
		res = FALSE;	
leo_sobral@1
   765
		goto done;
leo_sobral@1
   766
    }
leo_sobral@1
   767
leo_sobral@1
   768
    res = g_str_has_prefix (response->str, "ACCEPT");
leo_sobral@1
   769
    if (!res) {
leo_sobral@1
   770
		g_warning ("[%s] Protocol version request error: %s", __FUNCTION__, response->str);
leo_sobral@1
   771
	/* get the version number returned by the REJECT message */
leo_sobral@1
   772
	if ( ( res = g_str_has_prefix (response->str, "REJECT") ) == TRUE ) {
renatofilho@147
   773
        gchar *new_version = NULL;
leo_sobral@1
   774
	    new_version = g_strrstr( response->str, "]" );
leo_sobral@1
   775
	    if (new_version!=NULL) {
leo_sobral@1
   776
		++new_version; /* skip ']' character */
leo_sobral@1
   777
		if ( new_version != NULL ) {
renatofilho@131
   778
		    gmyth_debug ( "[%s] got MythTV version = %s.\n", __FUNCTION__, new_version );
renatofilho@147
   779
		    mythtv_version = (gint)g_ascii_strtoull (new_version, NULL, 10 );
leo_sobral@1
   780
		    /* do reconnection to the socket (socket is closed if the MythTV version was wrong) */
melunko@112
   781
		    gmyth_socket_connect( gmyth_socket, gmyth_socket->hostname, gmyth_socket->port );
renatofilho@147
   782
            new_version =NULL;
rosfran@101
   783
		    if ( --max_iterations > 0 ) 
rosfran@101
   784
		    	goto try_new_version;
rosfran@101
   785
		    else
rosfran@101
   786
		    	goto done;
leo_sobral@1
   787
		}
leo_sobral@1
   788
	    }
leo_sobral@1
   789
	}
leo_sobral@1
   790
    }
rosfran@101
   791
    
rosfran@101
   792
    /* change the return value to a valid one */
rosfran@105
   793
    if ( res ) {
rosfran@101
   794
    	mythtv_new_version = mythtv_version;
rosfran@105
   795
    	gmyth_socket->mythtv_version = mythtv_new_version;
rosfran@105
   796
    }
leo_sobral@1
   797
leo_sobral@1
   798
done:
leo_sobral@1
   799
    if ( payload != NULL )
leo_sobral@1
   800
		g_string_free (payload, TRUE);
leo_sobral@1
   801
    if ( response != NULL )
leo_sobral@1
   802
		g_string_free (response, TRUE);
leo_sobral@1
   803
rosfran@101
   804
    return mythtv_new_version;
leo_sobral@1
   805
}
leo_sobral@1
   806
leo_sobral@1
   807
/** Verifies if the Mythtv backend supported the GMyth supported version.
leo_sobral@1
   808
 * 
leo_sobral@1
   809
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   810
 * @return TRUE if supports, FALSE if not.
leo_sobral@1
   811
 */
leo_sobral@1
   812
gboolean
leo_sobral@1
   813
gmyth_socket_check_protocol_version (GMythSocket *gmyth_socket)
leo_sobral@1
   814
{
rosfran@101
   815
    return ( ( gmyth_socket->mythtv_version = 
rosfran@101
   816
    		gmyth_socket_check_protocol_version_number ( gmyth_socket, 
rosfran@101
   817
    							MYTHTV_VERSION_DEFAULT ) ) != MYTHTV_CANNOT_NEGOTIATE_VERSION );
rosfran@101
   818
}
rosfran@101
   819
rosfran@101
   820
/** Returns the Mythtv backend supported version.
rosfran@101
   821
 * 
rosfran@101
   822
 * @param gmyth_socket The GMythSocket instance.
rosfran@101
   823
 * @return The actual MythTV version number.
rosfran@101
   824
 */
rosfran@101
   825
gint
rosfran@101
   826
gmyth_socket_get_protocol_version (GMythSocket *gmyth_socket) 
rosfran@101
   827
{
rosfran@101
   828
	return gmyth_socket->mythtv_version;
leo_sobral@1
   829
}
leo_sobral@1
   830
leo_sobral@1
   831
/** Receives a backend answer after a gmyth_socket_send_command_call ().
leo_sobral@1
   832
 * 
leo_sobral@1
   833
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   834
 * @return The response received, or NULL if error or nothing was received.
leo_sobral@1
   835
 */
leo_sobral@1
   836
GString*
leo_sobral@1
   837
gmyth_socket_receive_response(GMythSocket *gmyth_socket)
leo_sobral@1
   838
{
leo_sobral@1
   839
    GIOStatus io_status = G_IO_STATUS_NORMAL;
leo_sobral@1
   840
    GError* error = NULL;
rosfran@154
   841
    gchar *buffer;
leo_sobral@1
   842
leo_sobral@1
   843
    GString *str = NULL;
leo_sobral@1
   844
leo_sobral@1
   845
    gsize bytes_read = 0;
leo_sobral@1
   846
    gint  len = 0;
leo_sobral@1
   847
    GIOCondition io_cond;
leo_sobral@1
   848
leo_sobral@1
   849
    g_return_val_if_fail( gmyth_socket != NULL, NULL );
leo_sobral@1
   850
leo_sobral@1
   851
    /* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   852
leo_sobral@1
   853
    g_static_mutex_lock( &mutex );
leo_sobral@1
   854
rosfran@154
   855
    //buffer = g_new0 (gchar, MYTH_PROTOCOL_FIELD_SIZE);
rosfran@154
   856
    buffer = g_strnfill (MYTH_PROTOCOL_FIELD_SIZE, ' ');
renatofilho@147
   857
    io_status = g_io_channel_read_chars (gmyth_socket->sd_io_ch, buffer, MYTH_PROTOCOL_FIELD_SIZE, &bytes_read, &error);
leo_sobral@1
   858
leo_sobral@1
   859
    /* verify if the input (read) buffer is ready to receive data */
renatofilho@147
   860
    io_cond = g_io_channel_get_buffer_condition (gmyth_socket->sd_io_ch);
leo_sobral@1
   861
renatofilho@131
   862
    gmyth_debug ( "[%s] Bytes read = %d\n", __FUNCTION__, bytes_read );
leo_sobral@1
   863
leo_sobral@1
   864
    if( (io_status == G_IO_STATUS_ERROR) || (bytes_read <= 0) ) {
leo_sobral@1
   865
		g_warning ("[%s] Error in mythprotocol response from backend\n", __FUNCTION__);
leo_sobral@1
   866
		str = NULL;
leo_sobral@1
   867
		//return NULL;
leo_sobral@1
   868
    } else {
leo_sobral@1
   869
leo_sobral@1
   870
		io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error );
leo_sobral@1
   871
		/* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   872
		io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
leo_sobral@1
   873
	
rosfran@154
   874
		//if ( ( io_cond & G_IO_IN ) != 0 ) {
rosfran@154
   875
            //gchar *buffer_aux = NULL;
renatofilho@147
   876
renatofilho@147
   877
		    /* removes trailing whitespace */
rosfran@154
   878
		    //buffer_aux = g_strstrip (buffer);
rosfran@154
   879
		    len = (gint)g_ascii_strtoull ( g_strstrip (buffer), NULL, 10 );
renatofilho@147
   880
renatofilho@147
   881
            if (buffer != NULL) {
renatofilho@147
   882
                g_free (buffer);
renatofilho@147
   883
                buffer = NULL;
renatofilho@147
   884
            }
rosfran@154
   885
            
rosfran@154
   886
            /*            
rosfran@154
   887
            if (buffer_aux != NULL) {
rosfran@154
   888
                g_free (buffer_aux);
rosfran@154
   889
                buffer_aux = NULL;
rosfran@154
   890
            }
rosfran@154
   891
            */
renatofilho@147
   892
renatofilho@147
   893
            buffer = g_new0 (gchar, len+1);
leo_sobral@1
   894
	
leo_sobral@1
   895
		    bytes_read = 0;
renatofilho@147
   896
		    io_status = g_io_channel_read_chars( gmyth_socket->sd_io_ch, buffer, len, &bytes_read, &error);
leo_sobral@1
   897
		    buffer[bytes_read] = '\0';
rosfran@154
   898
		//}
leo_sobral@1
   899
    }  
leo_sobral@1
   900
leo_sobral@1
   901
    g_static_mutex_unlock( &mutex );
leo_sobral@1
   902
renatofilho@131
   903
    gmyth_debug ("[%s] Response received from backend: {%s}\n", __FUNCTION__, buffer);
leo_sobral@1
   904
leo_sobral@1
   905
    if ( ( bytes_read != len ) || ( io_status == G_IO_STATUS_ERROR ) )
leo_sobral@1
   906
		str = NULL;
leo_sobral@1
   907
    else
renatofilho@147
   908
		str = g_string_new (buffer);
leo_sobral@1
   909
leo_sobral@1
   910
    if ( error != NULL ) {
leo_sobral@1
   911
		g_printerr( "[%s] Error found receiving response from the IO channel: (%d, %s)\n", __FUNCTION__, error->code, error->message );
leo_sobral@1
   912
		str = NULL;
renatofilho@147
   913
		g_error_free (error);
leo_sobral@1
   914
    }
leo_sobral@1
   915
renatofilho@147
   916
    g_free (buffer);
leo_sobral@1
   917
    return str;
leo_sobral@1
   918
}
leo_sobral@1
   919
leo_sobral@1
   920
/** Format a Mythtv command from the str_list entries and send it to backend.
leo_sobral@1
   921
 * 
leo_sobral@1
   922
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   923
 * @param str_list The string list to form the command
leo_sobral@1
   924
 * @return TRUE if command was sent, FALSE if any error happens.
leo_sobral@1
   925
 */
leo_sobral@1
   926
gboolean
leo_sobral@1
   927
gmyth_socket_write_stringlist(GMythSocket *gmyth_socket, GMythStringList* str_list)
leo_sobral@1
   928
{
leo_sobral@1
   929
renatofilho@155
   930
    GList *tmp_list = NULL;
renatofilho@155
   931
    GPtrArray *ptr_array = NULL;
renatofilho@155
   932
    gchar *str_array = NULL;
leo_sobral@1
   933
leo_sobral@1
   934
    g_static_mutex_lock( &mutex );
leo_sobral@1
   935
renatofilho@155
   936
    ptr_array = g_ptr_array_sized_new (g_list_length(str_list->glist));
leo_sobral@1
   937
renatofilho@131
   938
    gmyth_debug ( "[%s] Number of parameters = %d\n", __FUNCTION__, g_list_length(str_list->glist) );    
leo_sobral@1
   939
leo_sobral@1
   940
    // FIXME: change this implementation!
leo_sobral@1
   941
    tmp_list = str_list->glist;
leo_sobral@1
   942
    for(; tmp_list; tmp_list = tmp_list->next) {
melunko@125
   943
	if ( tmp_list->data != NULL ) {
leo_sobral@1
   944
	    g_ptr_array_add(ptr_array, ((GString*)tmp_list->data)->str);
melunko@125
   945
	} else {
renatofilho@155
   946
	    g_ptr_array_add (ptr_array, "");
melunko@125
   947
	}
leo_sobral@1
   948
    }
leo_sobral@1
   949
    g_ptr_array_add(ptr_array, NULL); // g_str_joinv() needs a NULL terminated string
leo_sobral@1
   950
leo_sobral@1
   951
    str_array = g_strjoinv (MYTH_SEPARATOR, (gchar **) (ptr_array->pdata));
leo_sobral@1
   952
leo_sobral@1
   953
    g_static_mutex_unlock( &mutex );
leo_sobral@1
   954
renatofilho@131
   955
    gmyth_debug ( "[%s]\t\tSending the String list = %s\n", __FUNCTION__, str_array );
rosfran@29
   956
leo_sobral@1
   957
    // Sends message to backend	
renatofilho@155
   958
    // TODO: implement looping to send remaining data, and add timeout testing!    
renatofilho@155
   959
    GString *command = g_string_new(str_array);
renatofilho@155
   960
    gmyth_socket_send_command(gmyth_socket, command);
renatofilho@155
   961
    g_string_free (command, TRUE);
leo_sobral@1
   962
leo_sobral@1
   963
    g_free (str_array);
leo_sobral@1
   964
    g_ptr_array_free (ptr_array, TRUE);
leo_sobral@1
   965
leo_sobral@1
   966
    return TRUE;
leo_sobral@1
   967
}
leo_sobral@1
   968
leo_sobral@1
   969
/* Receives a backend command response and split it into the given string list.
leo_sobral@1
   970
 * 
leo_sobral@1
   971
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   972
 * @param str_list the string list to be filled.
leo_sobral@1
   973
 * @return The number of received strings.
leo_sobral@1
   974
 */
leo_sobral@1
   975
gint
leo_sobral@1
   976
gmyth_socket_read_stringlist (GMythSocket *gmyth_socket, GMythStringList* str_list)
leo_sobral@1
   977
{
leo_sobral@1
   978
    GString *response;
leo_sobral@1
   979
    gchar **str_array;
leo_sobral@1
   980
    gint i;
leo_sobral@1
   981
leo_sobral@1
   982
    response = gmyth_socket_receive_response(gmyth_socket);
leo_sobral@1
   983
    g_static_mutex_lock( &mutex );
leo_sobral@1
   984
leo_sobral@1
   985
    gmyth_string_list_clear_all (str_list);	
leo_sobral@1
   986
    str_array = g_strsplit (response->str, MYTH_SEPARATOR, -1);
leo_sobral@1
   987
leo_sobral@1
   988
    for (i=0; i< g_strv_length (str_array); i++) {
rosfran@154
   989
	gmyth_string_list_append_char_array (str_list, str_array[i] );
leo_sobral@1
   990
    }
leo_sobral@1
   991
    g_static_mutex_unlock( &mutex );
leo_sobral@1
   992
leo_sobral@1
   993
    g_string_free (response, TRUE);
leo_sobral@1
   994
    g_strfreev (str_array);
leo_sobral@1
   995
leo_sobral@1
   996
    return gmyth_string_list_length (str_list);
leo_sobral@1
   997
}
leo_sobral@1
   998
leo_sobral@1
   999
/** Formats a Mythtv protocol command based on str_list and sends it to
leo_sobral@1
  1000
 * the connected backend. The backend response is overwritten into str_list.
leo_sobral@1
  1001
 *
leo_sobral@1
  1002
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1003
 * @param str_list The string list to be sent, and on which the answer 
leo_sobral@1
  1004
 * will be written.
leo_sobral@1
  1005
 * @return TRUE if command was sent and an answer was received, FALSE if any
leo_sobral@1
  1006
 * error happens.
leo_sobral@1
  1007
 */
leo_sobral@1
  1008
gint
leo_sobral@1
  1009
gmyth_socket_sendreceive_stringlist (GMythSocket *gmyth_socket, GMythStringList *str_list)
leo_sobral@1
  1010
{
leo_sobral@1
  1011
    gmyth_socket_write_stringlist (gmyth_socket, str_list);
leo_sobral@1
  1012
leo_sobral@1
  1013
    return gmyth_socket_read_stringlist (gmyth_socket, str_list);
leo_sobral@1
  1014
}