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