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