gmyth/src/gmyth_socket.c
author rosfran
Tue Dec 19 21:02:12 2006 +0000 (2006-12-19)
branchtrunk
changeset 229 5ed97410d052
parent 228 0c4b132db3e7
child 230 1c6948679ddf
permissions -rw-r--r--
[svn r230] A lot of memory clean-ups.
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@177
   310
    if ( local_hostname != NULL )
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
	
rosfran@105
   475
	  *err = select( FD_SETSIZE, NULL, &fd_w, NULL, timeout);
rosfran@105
   476
	  if ( *err < 0 ) {
rosfran@105
   477
	    g_warning( "[%s] Connection unsucessfull (timed out).\n", __FUNCTION__ );
rosfran@105
   478
	    *err=errno;
rosfran@105
   479
	    return -1;
rosfran@105
   480
	  }
rosfran@105
   481
	
rosfran@105
   482
	  /* 0 means it timeout out & no fds changed */
rosfran@105
   483
	  if(*err==0) {
rosfran@105
   484
	    close(fd);
rosfran@105
   485
	    *err=ETIMEDOUT;
rosfran@105
   486
	    return -1;
rosfran@105
   487
	  }
rosfran@105
   488
	
rosfran@105
   489
	  /* Get the return code from the connect */
rosfran@105
   490
	  len = sizeof( ret );
renatofilho@206
   491
	  *err=getsockopt( fd, SOL_SOCKET, SO_ERROR, &ret, (socklen_t *) &len);
rosfran@105
   492
	  
rosfran@105
   493
	  if( *err < 0 ) {
rosfran@105
   494
	    g_warning( "[%s] Connection usnsucessfull.\n", __FUNCTION__ );
rosfran@105
   495
	    *err=errno;
rosfran@105
   496
	    return -1;
rosfran@105
   497
	  }
rosfran@105
   498
	
rosfran@105
   499
	  /* ret=0 means success, otherwise it contains the errno */
rosfran@105
   500
	  if (ret) {
rosfran@105
   501
	    *err=ret;
rosfran@105
   502
	    return -1;
rosfran@105
   503
	  }
rosfran@105
   504
	
rosfran@105
   505
	  *err=0;
rosfran@105
   506
	  return 0;
rosfran@105
   507
}
rosfran@105
   508
leo_sobral@1
   509
/** Connects to the backend.
leo_sobral@1
   510
 * 
leo_sobral@1
   511
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   512
 * @param hostname The backend hostname or IP address.
leo_sobral@1
   513
 * @param port The backend port.
leo_sobral@1
   514
 * @return TRUE if success, FALSE if error.
leo_sobral@1
   515
 */
leo_sobral@1
   516
gboolean
melunko@112
   517
gmyth_socket_connect (GMythSocket *gmyth_socket,
melunko@158
   518
	const gchar *hostname, gint port)
leo_sobral@1
   519
{
leo_sobral@1
   520
    struct addrinfo *addr_info_data = NULL, *addr_info0 = NULL;
leo_sobral@1
   521
    gint ret_code = -1;
leo_sobral@1
   522
    gint errno;
leo_sobral@1
   523
    gboolean ret = TRUE;
leo_sobral@1
   524
rosfran@225
   525
    gmyth_debug ("CONNECTING %s:%d", hostname, port);
rosfran@225
   526
leo_sobral@1
   527
    if ( hostname == NULL )
rosfran@225
   528
			gmyth_debug ( "[%s] Invalid hostname parameter!\n", __FUNCTION__ );
leo_sobral@1
   529
leo_sobral@1
   530
    /* store hostname and port number */
renatofilho@206
   531
    if (gmyth_socket->hostname != NULL) {
rosfran@226
   532
        //g_free (gmyth_socket->hostname);
renatofilho@206
   533
        gmyth_socket->hostname = NULL;
renatofilho@206
   534
    }
renatofilho@206
   535
rosfran@225
   536
    errno = gmyth_socket_toaddrinfo ( hostname, port, &addr_info_data );
rosfran@225
   537
rosfran@225
   538
    g_return_val_if_fail( addr_info_data != NULL && hostname != NULL, FALSE );
rosfran@225
   539
melunko@112
   540
    gmyth_socket->hostname = g_strdup( hostname );
melunko@112
   541
    gmyth_socket->port = port;
leo_sobral@1
   542
leo_sobral@1
   543
    for ( addr_info0 = addr_info_data; addr_info0; addr_info0 = addr_info_data->ai_next ) {
renatofilho@147
   544
        /* init socket descriptor */
renatofilho@147
   545
        gmyth_socket->sd = socket( addr_info0->ai_family, addr_info0->ai_socktype,
renatofilho@147
   546
                         addr_info0->ai_protocol );
leo_sobral@1
   547
renatofilho@147
   548
        if ( gmyth_socket->sd < 0 )
renatofilho@147
   549
            continue;
leo_sobral@1
   550
renatofilho@206
   551
        struct timeval timeout;
renatofilho@147
   552
        
renatofilho@147
   553
        /* using 40 seconds timeout */
renatofilho@206
   554
        timeout.tv_sec = 40;
renatofilho@206
   555
        timeout.tv_usec = 0;
renatofilho@147
   556
            
renatofilho@206
   557
        if (gmyth_socket_try_connect (gmyth_socket->sd, (struct sockaddr *)addr_info0->ai_addr,
renatofilho@206
   558
                addr_info0->ai_addrlen, &timeout, &ret_code ) < 0 )
renatofilho@147
   559
        {
renatofilho@147
   560
            g_printerr( "[%s] Error connecting to backend!\n", __FUNCTION__ );
renatofilho@206
   561
            if (ret_code == ETIMEDOUT)
renatofilho@206
   562
                g_printerr( "[%s]\tBackend host unreachable!\n", __FUNCTION__ );
leo_sobral@1
   563
renatofilho@206
   564
            close (gmyth_socket->sd);
renatofilho@206
   565
            gmyth_socket->sd = -1;
renatofilho@206
   566
            g_printerr ("ERROR: %s\n", gai_strerror(ret_code));
renatofilho@147
   567
            continue;
renatofilho@147
   568
        }
leo_sobral@1
   569
renatofilho@147
   570
        /* only will be reached if none of the error above occurred */
renatofilho@147
   571
        break;
leo_sobral@1
   572
    }
renatofilho@147
   573
    freeaddrinfo (addr_info_data);
renatofilho@147
   574
    addr_info_data = NULL;
leo_sobral@1
   575
renatofilho@206
   576
    if (gmyth_socket->sd_io_ch != NULL) {
renatofilho@206
   577
        g_io_channel_unref (gmyth_socket->sd_io_ch);
renatofilho@206
   578
        gmyth_socket->sd_io_ch = NULL;
renatofilho@206
   579
    }
renatofilho@206
   580
renatofilho@206
   581
    gmyth_socket->sd_io_ch = g_io_channel_unix_new (gmyth_socket->sd);
rosfran@121
   582
    
rosfran@199
   583
    //GIOFlags flags = g_io_channel_get_flags (gmyth_socket->sd_io_ch);
rosfran@121
   584
		/* unset the nonblock flag */
rosfran@199
   585
		//flags &= ~G_IO_FLAG_NONBLOCK;
rosfran@121
   586
		/* unset the nonblocking stuff for some time, because GNUTLS doesn't like
rosfran@121
   587
		 * that */
rosfran@199
   588
		//g_io_channel_set_flags (gmyth_socket->sd_io_ch, flags, NULL);
leo_sobral@1
   589
leo_sobral@1
   590
    ret = ( ret_code == 0 ) ? TRUE : FALSE ;
leo_sobral@1
   591
    return ret;
leo_sobral@1
   592
}
leo_sobral@1
   593
leo_sobral@1
   594
/** Gets the GIOChannel associated to the given GMythSocket.
leo_sobral@1
   595
 * 
leo_sobral@1
   596
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   597
 */
leo_sobral@1
   598
GIOChannel *
leo_sobral@1
   599
gmyth_socket_get_io_channel( GMythSocket *gmyth_socket )
leo_sobral@1
   600
{
leo_sobral@1
   601
    g_return_val_if_fail( gmyth_socket != NULL, NULL );
leo_sobral@1
   602
leo_sobral@1
   603
    return gmyth_socket->sd_io_ch;
leo_sobral@1
   604
}
leo_sobral@1
   605
leo_sobral@1
   606
/** Verifies if the socket is able to read.
leo_sobral@1
   607
 * 
leo_sobral@1
   608
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   609
 * @return TRUE if the socket is able to read, FALSE if not.
leo_sobral@1
   610
 */
leo_sobral@1
   611
gboolean
leo_sobral@1
   612
gmyth_socket_is_able_to_read( GMythSocket *gmyth_socket )
leo_sobral@1
   613
{
leo_sobral@1
   614
    gboolean ret = TRUE;
leo_sobral@1
   615
leo_sobral@1
   616
    /* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   617
    GIOCondition io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
leo_sobral@1
   618
leo_sobral@1
   619
    if ( ( io_cond & G_IO_IN ) == 0 ) {
leo_sobral@1
   620
	g_warning ("[%s] IO channel is not able to send data!\n", __FUNCTION__);
leo_sobral@1
   621
	ret = FALSE;
leo_sobral@1
   622
    }
leo_sobral@1
   623
leo_sobral@1
   624
    return ret;
leo_sobral@1
   625
leo_sobral@1
   626
}
leo_sobral@1
   627
leo_sobral@1
   628
/** Verifies if the socket is able to write.
leo_sobral@1
   629
 * 
leo_sobral@1
   630
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   631
 * @return TRUE if the socket is able to write, FALSE if not.
leo_sobral@1
   632
 */
leo_sobral@1
   633
gboolean
leo_sobral@1
   634
gmyth_socket_is_able_to_write( GMythSocket *gmyth_socket )
leo_sobral@1
   635
{
leo_sobral@1
   636
    gboolean ret = TRUE;
leo_sobral@1
   637
leo_sobral@1
   638
    /* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   639
    GIOCondition io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
leo_sobral@1
   640
leo_sobral@1
   641
    if ( ( ( io_cond & G_IO_OUT ) == 0 ) || ( ( io_cond & G_IO_HUP ) == 0 ) ) {
leo_sobral@1
   642
	g_warning ("[%s] IO channel is not able to send data!\n", __FUNCTION__);
leo_sobral@1
   643
	ret = FALSE;
leo_sobral@1
   644
    }
leo_sobral@1
   645
leo_sobral@1
   646
    return ret;
leo_sobral@1
   647
leo_sobral@1
   648
}
leo_sobral@1
   649
leo_sobral@1
   650
/** Sends a command to the backend.
leo_sobral@1
   651
 * 
leo_sobral@1
   652
 * @param gmyth_socket the GMythSocket instance.
leo_sobral@1
   653
 * @param command The string command to be sent.
leo_sobral@1
   654
 */
leo_sobral@1
   655
gboolean
rosfran@101
   656
gmyth_socket_send_command(GMythSocket *gmyth_socket, GString *command) 
leo_sobral@1
   657
{
leo_sobral@1
   658
    gboolean ret = TRUE;
leo_sobral@1
   659
leo_sobral@1
   660
    GIOStatus io_status = G_IO_STATUS_NORMAL;
leo_sobral@1
   661
    //GIOCondition io_cond;
leo_sobral@1
   662
    GError* error = NULL;
renatofilho@206
   663
   
renatofilho@206
   664
    
leo_sobral@1
   665
    gchar *buffer = NULL;
leo_sobral@1
   666
leo_sobral@1
   667
    gsize bytes_written = 0;
leo_sobral@1
   668
rosfran@29
   669
    if( command == NULL || ( command->len <= 0 ) || command->str == NULL ) {
leo_sobral@1
   670
		g_warning ("[%s] Invalid NULL command parameter!\n", __FUNCTION__);
leo_sobral@1
   671
		ret = FALSE;
leo_sobral@1
   672
		goto done;
leo_sobral@1
   673
    }
leo_sobral@1
   674
rosfran@205
   675
    //g_static_mutex_lock( &mutex );
renatofilho@131
   676
    gmyth_debug ("[%s] Sending command to backend: %s\n", __FUNCTION__, command->str);
leo_sobral@1
   677
leo_sobral@1
   678
    buffer = g_strnfill( BUFLEN, ' ' );
rosfran@154
   679
    g_snprintf( buffer, MYTH_PROTOCOL_FIELD_SIZE+1, "%-8d", command->len);
renatofilho@131
   680
    gmyth_debug ( "[%s] buffer = [%s]\n", __FUNCTION__, buffer  );
leo_sobral@1
   681
leo_sobral@1
   682
    command = g_string_prepend(command, buffer);
leo_sobral@1
   683
renatofilho@131
   684
    gmyth_debug ( "[%s] command = [%s]\n", __FUNCTION__, command->str  );
leo_sobral@1
   685
leo_sobral@1
   686
    /* write bytes to socket */    
leo_sobral@1
   687
    io_status = g_io_channel_write_chars( gmyth_socket->sd_io_ch, command->str, 
leo_sobral@1
   688
	    command->len, &bytes_written, &error );
leo_sobral@1
   689
leo_sobral@1
   690
leo_sobral@1
   691
    if( (io_status == G_IO_STATUS_ERROR) || ( bytes_written <= 0 ) ) {
leo_sobral@1
   692
		g_warning ("[%s] Error while writing to socket", __FUNCTION__);
leo_sobral@1
   693
		ret = FALSE;
leo_sobral@1
   694
    } else if ( bytes_written < command->len ) {
leo_sobral@1
   695
		g_warning ("[%s] Not all data was written socket", __FUNCTION__);
leo_sobral@1
   696
		ret = FALSE;
leo_sobral@1
   697
    }
leo_sobral@1
   698
leo_sobral@1
   699
    io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error );
leo_sobral@1
   700
leo_sobral@1
   701
    if ( ( bytes_written != command->len ) || ( io_status == G_IO_STATUS_ERROR ) )
leo_sobral@1
   702
    {
leo_sobral@1
   703
		g_warning ("[%s] Some problem occurred when sending data to the socket\n", __FUNCTION__);
leo_sobral@1
   704
	
leo_sobral@1
   705
		ret = TRUE;
leo_sobral@1
   706
    }
leo_sobral@1
   707
rosfran@205
   708
    //g_static_mutex_unlock( &mutex );
leo_sobral@1
   709
done:
leo_sobral@1
   710
    if ( error != NULL ) {
leo_sobral@1
   711
		g_printerr( "[%s] Error found reading data from IO channel: (%d, %s)\n", __FUNCTION__, error->code, error->message );
leo_sobral@1
   712
		ret = FALSE;
leo_sobral@1
   713
		g_error_free( error );
leo_sobral@1
   714
    }
leo_sobral@1
   715
leo_sobral@1
   716
    if ( buffer!= NULL )
leo_sobral@1
   717
		g_free( buffer );
leo_sobral@1
   718
leo_sobral@1
   719
    return ret;
leo_sobral@1
   720
}
leo_sobral@1
   721
leo_sobral@1
   722
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
leo_sobral@1
   723
 * supported by the backend and send the "ANN" command.
leo_sobral@1
   724
 * 
leo_sobral@1
   725
 * @param gmyth_socket the GMythSocket instance.
leo_sobral@1
   726
 * @param hostname_backend The backend hostname or IP address.
leo_sobral@1
   727
 * @param port The backend port to connect.
rosfran@177
   728
 * @param blocking_client A flag to choose between blocking and non-blocking
rosfran@177
   729
 * @param with_events	Sets the connection flag to receive events.
rosfran@177
   730
 * 										backend connection. 
leo_sobral@1
   731
 */
rosfran@177
   732
static gboolean
rosfran@177
   733
gmyth_socket_connect_to_backend_and_events (GMythSocket *gmyth_socket, 
rosfran@177
   734
	const gchar *hostname_backend, gint port, gboolean blocking_client,
rosfran@177
   735
	gboolean with_events)
leo_sobral@1
   736
{
melunko@112
   737
    if (!gmyth_socket_connect (gmyth_socket, hostname_backend, port)) {
rosfran@123
   738
		g_warning ("[%s] Could not open socket to backend machine [%s]\n", __FUNCTION__,
rosfran@123
   739
					hostname_backend );
leo_sobral@1
   740
		return FALSE;
leo_sobral@1
   741
    }
leo_sobral@1
   742
rosfran@101
   743
    if ( gmyth_socket_check_protocol_version (gmyth_socket) ) {
leo_sobral@1
   744
leo_sobral@1
   745
	GString *result;
leo_sobral@1
   746
	GString *base_str = g_string_new("");
leo_sobral@1
   747
	GString *hostname = NULL;
leo_sobral@1
   748
leo_sobral@1
   749
	hostname = gmyth_socket_get_local_hostname();
leo_sobral@1
   750
rosfran@177
   751
	g_string_printf(base_str, "ANN %s %s %u", 
leo_sobral@1
   752
		(blocking_client ? "Playback" : "Monitor"),
rosfran@177
   753
		hostname->str, with_events);
leo_sobral@1
   754
renatofilho@131
   755
	gmyth_debug ("[%s] Connection command sent to backend: %s", __FUNCTION__, base_str->str);
leo_sobral@1
   756
leo_sobral@1
   757
	gmyth_socket_send_command (gmyth_socket, base_str);
rosfran@177
   758
	result = gmyth_socket_receive_response (gmyth_socket);
leo_sobral@1
   759
leo_sobral@1
   760
	if (result != NULL) {
renatofilho@131
   761
	    gmyth_debug ("[%s] Response received from backend: %s", __FUNCTION__, result->str);
leo_sobral@1
   762
	    g_string_free (result, TRUE);
leo_sobral@1
   763
	}
leo_sobral@1
   764
rosfran@225
   765
	g_string_free (hostname, FALSE);
leo_sobral@1
   766
	g_string_free (base_str, TRUE);
leo_sobral@1
   767
leo_sobral@1
   768
	return TRUE;
leo_sobral@1
   769
    } else {
melunko@112
   770
	g_warning ("[%s] GMythSocket could not connect to the backend", __FUNCTION__);	
melunko@112
   771
	return FALSE;
melunko@112
   772
    }
leo_sobral@1
   773
}
leo_sobral@1
   774
rosfran@177
   775
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
rosfran@177
   776
 * supported by the backend and send the "ANN" command.
rosfran@177
   777
 * 
rosfran@177
   778
 * @param gmyth_socket the GMythSocket instance.
rosfran@177
   779
 * @param hostname_backend The backend hostname or IP address.
rosfran@177
   780
 * @param port The backend port to connect.
rosfran@177
   781
 * @param blocking_client A flag to choose between blocking and non-blocking 
rosfran@177
   782
 */
rosfran@177
   783
gboolean
rosfran@177
   784
gmyth_socket_connect_to_backend (GMythSocket *gmyth_socket, 
rosfran@177
   785
	const gchar *hostname_backend, gint port, gboolean blocking_client)
rosfran@177
   786
{
rosfran@177
   787
    if (!gmyth_socket_connect_to_backend_and_events ( gmyth_socket, hostname_backend, port,
rosfran@177
   788
    		blocking_client, FALSE) ) {
rosfran@177
   789
		gmyth_debug ("[%s] Could not open socket to backend machine [%s]\n", __FUNCTION__,
rosfran@177
   790
					hostname_backend );
rosfran@177
   791
		return FALSE;
rosfran@177
   792
    }
rosfran@177
   793
    
rosfran@177
   794
    return TRUE;
rosfran@177
   795
rosfran@177
   796
}
rosfran@177
   797
rosfran@177
   798
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
rosfran@177
   799
 * supported by the backend and send the "ANN" command.
rosfran@177
   800
 * 
rosfran@177
   801
 * @param gmyth_socket the GMythSocket instance.
rosfran@177
   802
 * @param hostname_backend The backend hostname or IP address.
rosfran@177
   803
 * @param port The backend port to connect.
rosfran@177
   804
 * @param blocking_client A flag to choose between blocking and non-blocking 
rosfran@177
   805
 */
rosfran@177
   806
gboolean
rosfran@177
   807
gmyth_socket_connect_to_backend_events (GMythSocket *gmyth_socket, 
rosfran@177
   808
	const gchar *hostname_backend, gint port, gboolean blocking_client)
rosfran@177
   809
{
rosfran@177
   810
    if (!gmyth_socket_connect_to_backend_and_events ( gmyth_socket, hostname_backend, port,
rosfran@177
   811
    		blocking_client, TRUE) ) {
rosfran@177
   812
		gmyth_debug ("[%s] Could not open socket to backend machine in order to receive events [%s]\n", __FUNCTION__,
rosfran@177
   813
					hostname_backend );
rosfran@177
   814
		return FALSE;
rosfran@177
   815
    }
rosfran@177
   816
    
rosfran@177
   817
    return TRUE;
rosfran@177
   818
}
rosfran@177
   819
leo_sobral@1
   820
/** Closes the socket connection to the backend.
leo_sobral@1
   821
 * 
leo_sobral@1
   822
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   823
 */
leo_sobral@1
   824
void
leo_sobral@1
   825
gmyth_socket_close_connection (GMythSocket *gmyth_socket)
leo_sobral@1
   826
{
leo_sobral@1
   827
    close (gmyth_socket->sd);	
renatofilho@206
   828
    gmyth_socket->sd = -1;
renatofilho@206
   829
renatofilho@206
   830
    if (gmyth_socket->sd_io_ch != NULL) {
renatofilho@206
   831
        g_io_channel_unref (gmyth_socket->sd_io_ch);
renatofilho@206
   832
        gmyth_socket->sd_io_ch = NULL;
renatofilho@206
   833
    }
leo_sobral@1
   834
}
leo_sobral@1
   835
leo_sobral@1
   836
leo_sobral@1
   837
/** Try the MythTV version numbers, and get the version returned by
leo_sobral@1
   838
 * the possible REJECT message, in order to contruct a new
leo_sobral@1
   839
 * MythTV version request.
leo_sobral@1
   840
 * 
leo_sobral@1
   841
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   842
 * @param mythtv_version The Mythtv protocol version to be tested
rosfran@101
   843
 * 
rosfran@101
   844
 * @return The actual MythTV the client is connected to.
leo_sobral@1
   845
 */
rosfran@101
   846
gint
leo_sobral@1
   847
gmyth_socket_check_protocol_version_number (GMythSocket *gmyth_socket, gint mythtv_version)
leo_sobral@1
   848
{
renatofilho@147
   849
    GString *response = NULL;
renatofilho@147
   850
    GString *payload = NULL;
leo_sobral@1
   851
    gboolean res = TRUE;
rosfran@101
   852
    gint mythtv_new_version = MYTHTV_CANNOT_NEGOTIATE_VERSION;
rosfran@101
   853
    guint max_iterations = MYTHTV_MAX_VERSION_CHECKS;
leo_sobral@1
   854
leo_sobral@1
   855
try_new_version:
leo_sobral@1
   856
    payload = g_string_new ("MYTH_PROTO_VERSION");
leo_sobral@1
   857
    g_string_append_printf( payload, " %d", mythtv_version );
leo_sobral@1
   858
leo_sobral@1
   859
    gmyth_socket_send_command(gmyth_socket, payload);
leo_sobral@1
   860
    response = gmyth_socket_receive_response(gmyth_socket);
leo_sobral@1
   861
leo_sobral@1
   862
    if (response == NULL) {
leo_sobral@1
   863
		g_warning ("[%s] Check protocol version error! Not answered!", __FUNCTION__);
leo_sobral@1
   864
		res = FALSE;	
leo_sobral@1
   865
		goto done;
leo_sobral@1
   866
    }
leo_sobral@1
   867
leo_sobral@1
   868
    res = g_str_has_prefix (response->str, "ACCEPT");
leo_sobral@1
   869
    if (!res) {
leo_sobral@1
   870
		g_warning ("[%s] Protocol version request error: %s", __FUNCTION__, response->str);
leo_sobral@1
   871
	/* get the version number returned by the REJECT message */
leo_sobral@1
   872
	if ( ( res = g_str_has_prefix (response->str, "REJECT") ) == TRUE ) {
renatofilho@147
   873
        gchar *new_version = NULL;
leo_sobral@1
   874
	    new_version = g_strrstr( response->str, "]" );
leo_sobral@1
   875
	    if (new_version!=NULL) {
leo_sobral@1
   876
		++new_version; /* skip ']' character */
leo_sobral@1
   877
		if ( new_version != NULL ) {
renatofilho@131
   878
		    gmyth_debug ( "[%s] got MythTV version = %s.\n", __FUNCTION__, new_version );
renatofilho@147
   879
		    mythtv_version = (gint)g_ascii_strtoull (new_version, NULL, 10 );
leo_sobral@1
   880
		    /* do reconnection to the socket (socket is closed if the MythTV version was wrong) */
melunko@112
   881
		    gmyth_socket_connect( gmyth_socket, gmyth_socket->hostname, gmyth_socket->port );
renatofilho@147
   882
            new_version =NULL;
rosfran@101
   883
		    if ( --max_iterations > 0 ) 
rosfran@101
   884
		    	goto try_new_version;
rosfran@101
   885
		    else
rosfran@101
   886
		    	goto done;
leo_sobral@1
   887
		}
leo_sobral@1
   888
	    }
leo_sobral@1
   889
	}
leo_sobral@1
   890
    }
rosfran@101
   891
    
rosfran@101
   892
    /* change the return value to a valid one */
rosfran@105
   893
    if ( res ) {
rosfran@101
   894
    	mythtv_new_version = mythtv_version;
rosfran@105
   895
    	gmyth_socket->mythtv_version = mythtv_new_version;
rosfran@105
   896
    }
leo_sobral@1
   897
leo_sobral@1
   898
done:
leo_sobral@1
   899
    if ( payload != NULL )
leo_sobral@1
   900
		g_string_free (payload, TRUE);
leo_sobral@1
   901
    if ( response != NULL )
leo_sobral@1
   902
		g_string_free (response, TRUE);
leo_sobral@1
   903
rosfran@101
   904
    return mythtv_new_version;
leo_sobral@1
   905
}
leo_sobral@1
   906
leo_sobral@1
   907
/** Verifies if the Mythtv backend supported the GMyth supported version.
leo_sobral@1
   908
 * 
leo_sobral@1
   909
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   910
 * @return TRUE if supports, FALSE if not.
leo_sobral@1
   911
 */
leo_sobral@1
   912
gboolean
leo_sobral@1
   913
gmyth_socket_check_protocol_version (GMythSocket *gmyth_socket)
leo_sobral@1
   914
{
rosfran@101
   915
    return ( ( gmyth_socket->mythtv_version = 
rosfran@101
   916
    		gmyth_socket_check_protocol_version_number ( gmyth_socket, 
rosfran@101
   917
    							MYTHTV_VERSION_DEFAULT ) ) != MYTHTV_CANNOT_NEGOTIATE_VERSION );
rosfran@101
   918
}
rosfran@101
   919
rosfran@101
   920
/** Returns the Mythtv backend supported version.
rosfran@101
   921
 * 
rosfran@101
   922
 * @param gmyth_socket The GMythSocket instance.
rosfran@101
   923
 * @return The actual MythTV version number.
rosfran@101
   924
 */
rosfran@101
   925
gint
rosfran@101
   926
gmyth_socket_get_protocol_version (GMythSocket *gmyth_socket) 
rosfran@101
   927
{
rosfran@101
   928
	return gmyth_socket->mythtv_version;
leo_sobral@1
   929
}
leo_sobral@1
   930
leo_sobral@1
   931
/** Receives a backend answer after a gmyth_socket_send_command_call ().
leo_sobral@1
   932
 * 
leo_sobral@1
   933
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   934
 * @return The response received, or NULL if error or nothing was received.
leo_sobral@1
   935
 */
leo_sobral@1
   936
GString*
leo_sobral@1
   937
gmyth_socket_receive_response(GMythSocket *gmyth_socket)
leo_sobral@1
   938
{
leo_sobral@1
   939
    GIOStatus io_status = G_IO_STATUS_NORMAL;
leo_sobral@1
   940
    GError* error = NULL;
rosfran@154
   941
    gchar *buffer;
leo_sobral@1
   942
leo_sobral@1
   943
    GString *str = NULL;
leo_sobral@1
   944
leo_sobral@1
   945
    gsize bytes_read = 0;
leo_sobral@1
   946
    gint  len = 0;
leo_sobral@1
   947
    GIOCondition io_cond;
leo_sobral@1
   948
leo_sobral@1
   949
    g_return_val_if_fail( gmyth_socket != NULL, NULL );
leo_sobral@1
   950
leo_sobral@1
   951
    /* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   952
rosfran@199
   953
    //g_static_mutex_lock( &mutex );
leo_sobral@1
   954
rosfran@154
   955
    //buffer = g_new0 (gchar, MYTH_PROTOCOL_FIELD_SIZE);
rosfran@154
   956
    buffer = g_strnfill (MYTH_PROTOCOL_FIELD_SIZE, ' ');
renatofilho@147
   957
    io_status = g_io_channel_read_chars (gmyth_socket->sd_io_ch, buffer, MYTH_PROTOCOL_FIELD_SIZE, &bytes_read, &error);
leo_sobral@1
   958
leo_sobral@1
   959
    /* verify if the input (read) buffer is ready to receive data */
renatofilho@147
   960
    io_cond = g_io_channel_get_buffer_condition (gmyth_socket->sd_io_ch);
leo_sobral@1
   961
renatofilho@131
   962
    gmyth_debug ( "[%s] Bytes read = %d\n", __FUNCTION__, bytes_read );
leo_sobral@1
   963
leo_sobral@1
   964
    if( (io_status == G_IO_STATUS_ERROR) || (bytes_read <= 0) ) {
leo_sobral@1
   965
		g_warning ("[%s] Error in mythprotocol response from backend\n", __FUNCTION__);
leo_sobral@1
   966
		str = NULL;
leo_sobral@1
   967
		//return NULL;
leo_sobral@1
   968
    } else {
leo_sobral@1
   969
leo_sobral@1
   970
		io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error );
leo_sobral@1
   971
		/* verify if the input (read) buffer is ready to receive data */
leo_sobral@1
   972
		io_cond = g_io_channel_get_buffer_condition( gmyth_socket->sd_io_ch );
leo_sobral@1
   973
	
rosfran@154
   974
		//if ( ( io_cond & G_IO_IN ) != 0 ) {
rosfran@154
   975
            //gchar *buffer_aux = NULL;
renatofilho@147
   976
renatofilho@147
   977
		    /* removes trailing whitespace */
rosfran@154
   978
		    //buffer_aux = g_strstrip (buffer);
rosfran@154
   979
		    len = (gint)g_ascii_strtoull ( g_strstrip (buffer), NULL, 10 );
renatofilho@147
   980
renatofilho@147
   981
            if (buffer != NULL) {
renatofilho@147
   982
                g_free (buffer);
renatofilho@147
   983
                buffer = NULL;
renatofilho@147
   984
            }
rosfran@154
   985
            
rosfran@154
   986
            /*            
rosfran@154
   987
            if (buffer_aux != NULL) {
rosfran@154
   988
                g_free (buffer_aux);
rosfran@154
   989
                buffer_aux = NULL;
rosfran@154
   990
            }
rosfran@154
   991
            */
renatofilho@147
   992
renatofilho@147
   993
            buffer = g_new0 (gchar, len+1);
leo_sobral@1
   994
	
leo_sobral@1
   995
		    bytes_read = 0;
renatofilho@147
   996
		    io_status = g_io_channel_read_chars( gmyth_socket->sd_io_ch, buffer, len, &bytes_read, &error);
leo_sobral@1
   997
		    buffer[bytes_read] = '\0';
rosfran@154
   998
		//}
leo_sobral@1
   999
    }  
leo_sobral@1
  1000
rosfran@199
  1001
    //g_static_mutex_unlock( &mutex );
leo_sobral@1
  1002
renatofilho@131
  1003
    gmyth_debug ("[%s] Response received from backend: {%s}\n", __FUNCTION__, buffer);
leo_sobral@1
  1004
leo_sobral@1
  1005
    if ( ( bytes_read != len ) || ( io_status == G_IO_STATUS_ERROR ) )
leo_sobral@1
  1006
		str = NULL;
leo_sobral@1
  1007
    else
renatofilho@147
  1008
		str = g_string_new (buffer);
leo_sobral@1
  1009
leo_sobral@1
  1010
    if ( error != NULL ) {
leo_sobral@1
  1011
		g_printerr( "[%s] Error found receiving response from the IO channel: (%d, %s)\n", __FUNCTION__, error->code, error->message );
leo_sobral@1
  1012
		str = NULL;
renatofilho@147
  1013
		g_error_free (error);
leo_sobral@1
  1014
    }
leo_sobral@1
  1015
renatofilho@147
  1016
    g_free (buffer);
leo_sobral@1
  1017
    return str;
leo_sobral@1
  1018
}
leo_sobral@1
  1019
leo_sobral@1
  1020
/** Format a Mythtv command from the str_list entries and send it to backend.
leo_sobral@1
  1021
 * 
leo_sobral@1
  1022
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1023
 * @param str_list The string list to form the command
leo_sobral@1
  1024
 * @return TRUE if command was sent, FALSE if any error happens.
leo_sobral@1
  1025
 */
leo_sobral@1
  1026
gboolean
leo_sobral@1
  1027
gmyth_socket_write_stringlist(GMythSocket *gmyth_socket, GMythStringList* str_list)
leo_sobral@1
  1028
{
leo_sobral@1
  1029
renatofilho@155
  1030
    GList *tmp_list = NULL;
renatofilho@155
  1031
    GPtrArray *ptr_array = NULL;
renatofilho@155
  1032
    gchar *str_array = NULL;
leo_sobral@1
  1033
leo_sobral@1
  1034
    g_static_mutex_lock( &mutex );
leo_sobral@1
  1035
renatofilho@155
  1036
    ptr_array = g_ptr_array_sized_new (g_list_length(str_list->glist));
leo_sobral@1
  1037
renatofilho@131
  1038
    gmyth_debug ( "[%s] Number of parameters = %d\n", __FUNCTION__, g_list_length(str_list->glist) );    
leo_sobral@1
  1039
leo_sobral@1
  1040
    // FIXME: change this implementation!
leo_sobral@1
  1041
    tmp_list = str_list->glist;
leo_sobral@1
  1042
    for(; tmp_list; tmp_list = tmp_list->next) {
melunko@125
  1043
	if ( tmp_list->data != NULL ) {
leo_sobral@1
  1044
	    g_ptr_array_add(ptr_array, ((GString*)tmp_list->data)->str);
melunko@125
  1045
	} else {
renatofilho@155
  1046
	    g_ptr_array_add (ptr_array, "");
melunko@125
  1047
	}
leo_sobral@1
  1048
    }
leo_sobral@1
  1049
    g_ptr_array_add(ptr_array, NULL); // g_str_joinv() needs a NULL terminated string
leo_sobral@1
  1050
leo_sobral@1
  1051
    str_array = g_strjoinv (MYTH_SEPARATOR, (gchar **) (ptr_array->pdata));
leo_sobral@1
  1052
leo_sobral@1
  1053
    g_static_mutex_unlock( &mutex );
leo_sobral@1
  1054
renatofilho@131
  1055
    gmyth_debug ( "[%s]\t\tSending the String list = %s\n", __FUNCTION__, str_array );
rosfran@29
  1056
leo_sobral@1
  1057
    // Sends message to backend	
renatofilho@155
  1058
    // TODO: implement looping to send remaining data, and add timeout testing!    
renatofilho@155
  1059
    GString *command = g_string_new(str_array);
renatofilho@155
  1060
    gmyth_socket_send_command(gmyth_socket, command);
renatofilho@155
  1061
    g_string_free (command, TRUE);
leo_sobral@1
  1062
leo_sobral@1
  1063
    g_free (str_array);
leo_sobral@1
  1064
    g_ptr_array_free (ptr_array, TRUE);
leo_sobral@1
  1065
leo_sobral@1
  1066
    return TRUE;
leo_sobral@1
  1067
}
leo_sobral@1
  1068
leo_sobral@1
  1069
/* Receives a backend command response and split it into the given string list.
leo_sobral@1
  1070
 * 
leo_sobral@1
  1071
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1072
 * @param str_list the string list to be filled.
leo_sobral@1
  1073
 * @return The number of received strings.
leo_sobral@1
  1074
 */
leo_sobral@1
  1075
gint
leo_sobral@1
  1076
gmyth_socket_read_stringlist (GMythSocket *gmyth_socket, GMythStringList* str_list)
leo_sobral@1
  1077
{
leo_sobral@1
  1078
    GString *response;
leo_sobral@1
  1079
    gchar **str_array;
leo_sobral@1
  1080
    gint i;
leo_sobral@1
  1081
leo_sobral@1
  1082
    response = gmyth_socket_receive_response(gmyth_socket);
leo_sobral@1
  1083
    g_static_mutex_lock( &mutex );
leo_sobral@1
  1084
leo_sobral@1
  1085
    gmyth_string_list_clear_all (str_list);	
leo_sobral@1
  1086
    str_array = g_strsplit (response->str, MYTH_SEPARATOR, -1);
leo_sobral@1
  1087
leo_sobral@1
  1088
    for (i=0; i< g_strv_length (str_array); i++) {
rosfran@154
  1089
	gmyth_string_list_append_char_array (str_list, str_array[i] );
leo_sobral@1
  1090
    }
leo_sobral@1
  1091
    g_static_mutex_unlock( &mutex );
leo_sobral@1
  1092
leo_sobral@1
  1093
    g_string_free (response, TRUE);
leo_sobral@1
  1094
    g_strfreev (str_array);
leo_sobral@1
  1095
leo_sobral@1
  1096
    return gmyth_string_list_length (str_list);
leo_sobral@1
  1097
}
leo_sobral@1
  1098
leo_sobral@1
  1099
/** Formats a Mythtv protocol command based on str_list and sends it to
leo_sobral@1
  1100
 * the connected backend. The backend response is overwritten into str_list.
leo_sobral@1
  1101
 *
leo_sobral@1
  1102
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1103
 * @param str_list The string list to be sent, and on which the answer 
leo_sobral@1
  1104
 * will be written.
leo_sobral@1
  1105
 * @return TRUE if command was sent and an answer was received, FALSE if any
leo_sobral@1
  1106
 * error happens.
leo_sobral@1
  1107
 */
leo_sobral@1
  1108
gint
leo_sobral@1
  1109
gmyth_socket_sendreceive_stringlist (GMythSocket *gmyth_socket, GMythStringList *str_list)
leo_sobral@1
  1110
{
leo_sobral@1
  1111
    gmyth_socket_write_stringlist (gmyth_socket, str_list);
leo_sobral@1
  1112
leo_sobral@1
  1113
    return gmyth_socket_read_stringlist (gmyth_socket, str_list);
leo_sobral@1
  1114
}