gmyth/src/gmyth_file_transfer.c
author leo_sobral
Fri Mar 23 15:26:38 2007 +0000 (2007-03-23)
branchtrunk
changeset 444 d6603c86582f
parent 439 ee220ab67ffd
child 446 d260ed30f4de
permissions -rwxr-xr-x
[svn r449] warnings removed
     1 /**
     2  * GMyth Library
     3  *
     4  * @file gmyth/gmyth_file_transfer.c
     5  * 
     6  * @brief <p> GMythFileTransfer deals with the file streaming media remote/local
     7  * transfering to the MythTV frontend.
     8  *
     9  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    10  * @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
    11  *
    12  *//*
    13  * 
    14  * This program is free software; you can redistribute it and/or modify
    15  * it under the terms of the GNU Lesser General Public License as published by
    16  * the Free Software Foundation; either version 2 of the License, or
    17  * (at your option) any later version.
    18  *
    19  * This program is distributed in the hope that it will be useful,
    20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    22  * GNU General Public License for more details.
    23  *
    24  * You should have received a copy of the GNU Lesser General Public License
    25  * along with this program; if not, write to the Free Software
    26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    27  *
    28  * GStreamer MythTV plug-in properties:
    29  * - location (backend server hostname/URL) [ex.: myth://192.168.1.73:28722/1000_1092091.nuv]
    30  * - path (qurl - remote file to be opened)
    31  * - port number *   
    32  */
    33  
    34 #ifdef HAVE_CONFIG_H
    35 #include "config.h"
    36 #endif
    37 
    38 #include "gmyth_file_transfer.h"
    39 #include "gmyth_recorder.h"
    40 #include "gmyth_util.h"
    41 #include "gmyth_socket.h"
    42 #include "gmyth_stringlist.h"
    43 #include "gmyth_debug.h"
    44 #include "gmyth_uri.h"
    45 #include "gmyth_marshal.h"
    46 
    47 #include <unistd.h>
    48 #include <glib.h>
    49 
    50 #include <arpa/inet.h>
    51 #include <sys/types.h>
    52 #include <sys/socket.h>
    53 #include <netdb.h>
    54 #include <errno.h>
    55 #include <stdlib.h>
    56 #include <assert.h>
    57 
    58 #define GMYTHTV_QUERY_HEADER		"QUERY_FILETRANSFER "
    59 
    60 /* default values to the file transfer parameters */
    61 #define GMYTHTV_USER_READ_AHEAD			TRUE
    62 #define GMYTHTV_RETRIES							-1
    63 #define GMYTHTV_FILE_SIZE						0
    64 
    65 #define GMYTHTV_BUFFER_SIZE					64*1024
    66 
    67 #define GMYTHTV_VERSION							30
    68 
    69 #define GMYTHTV_TRANSFER_MAX_WAITS	700
    70 
    71 #ifdef GMYTHTV_ENABLE_DEBUG
    72 #define GMYTHTV_ENABLE_DEBUG				1
    73 #else
    74 #undef GMYTHTV_ENABLE_DEBUG
    75 #endif
    76 
    77 /* this NDEBUG is to maintain compatibility with GMyth library */
    78 #ifndef NDEBUG
    79 #define GMYTHTV_ENABLE_DEBUG				1
    80 #endif
    81 
    82 enum myth_sock_types {
    83   GMYTH_PLAYBACK_TYPE = 0,
    84   GMYTH_MONITOR_TYPE,
    85   GMYTH_FILETRANSFER_TYPE,
    86   GMYTH_RINGBUFFER_TYPE
    87 };
    88 
    89 #define GMYTH_FILE_TRANSFER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GMYTH_FILE_TRANSFER_TYPE, GMythFileTransferPrivate))
    90 
    91 struct _GMythFileTransferPrivate {
    92 	GMythRecorder *recorder;
    93 	
    94 	gboolean do_next_program_chain;
    95 	gboolean disposed;
    96 };
    97 
    98 static void gmyth_file_transfer_class_init          (GMythFileTransferClass *klass);
    99 static void gmyth_file_transfer_init                (GMythFileTransfer *object);
   100 
   101 static void gmyth_file_transfer_dispose  (GObject *object);
   102 static void gmyth_file_transfer_finalize (GObject *object);
   103 
   104 static void gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   105 										gint msg_code, gpointer livetv_recorder );
   106 
   107 static gboolean gmyth_connect_to_backend (GMythFileTransfer *transfer);
   108 
   109 void gmyth_file_transfer_close( GMythFileTransfer *transfer );
   110 
   111 static gboolean myth_control_acquire_context( GMythFileTransfer *transfer, gboolean do_wait );
   112 
   113 static gboolean myth_control_release_context( GMythFileTransfer *transfer );
   114 
   115 G_DEFINE_TYPE(GMythFileTransfer, gmyth_file_transfer, G_TYPE_OBJECT)
   116 
   117 static void
   118 gmyth_file_transfer_class_init (GMythFileTransferClass *klass)
   119 {
   120   GObjectClass *gobject_class;
   121   GMythFileTransferClass *gtransfer_class;
   122 
   123   gobject_class = (GObjectClass *) klass;
   124   gtransfer_class = (GMythFileTransferClass *) gobject_class;
   125 
   126   gobject_class->dispose  = gmyth_file_transfer_dispose;
   127   gobject_class->finalize = gmyth_file_transfer_finalize;
   128   
   129   g_type_class_add_private (gobject_class, sizeof (GMythFileTransferPrivate));
   130   
   131 	gtransfer_class->program_info_changed_handler = gmyth_file_transfer_program_info_changed;
   132   
   133 	gtransfer_class->program_info_changed_handler_signal_id = 
   134 		  g_signal_new ( "program-info-changed",
   135 		                 G_TYPE_FROM_CLASS (gtransfer_class),
   136 		                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
   137 		                 0,
   138 										 NULL,
   139 										 NULL,
   140 										 gmyth_marshal_VOID__INT_POINTER,
   141 										 G_TYPE_NONE,
   142 										 2,
   143 										 G_TYPE_INT,
   144 										 G_TYPE_POINTER );
   145 
   146 }
   147 
   148 static void
   149 gmyth_file_transfer_init (GMythFileTransfer *transfer)
   150 { 
   151   g_return_if_fail( transfer != NULL );
   152 
   153   transfer->readposition = 0;
   154   transfer->filesize = GMYTHTV_FILE_SIZE;
   155   
   156   transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   157   
   158   transfer->priv->do_next_program_chain = FALSE;
   159   transfer->priv->recorder = NULL;
   160 
   161   transfer->control_sock = NULL;
   162   transfer->sock = NULL;
   163   
   164   transfer->mutex = g_mutex_new();
   165   
   166 	g_signal_connect ( G_OBJECT (transfer), "program-info-changed",
   167               (GCallback)(GMYTH_FILE_TRANSFER_GET_CLASS(transfer)->program_info_changed_handler),
   168               NULL );
   169 
   170 }
   171 
   172 static void
   173 gmyth_file_transfer_dispose  (GObject *object)
   174 {	
   175 	GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (object);
   176 
   177     if (transfer->priv->disposed) {
   178         /* If dispose did already run, return. */
   179         return;
   180     }
   181     
   182     /* Make sure dispose does not run twice. */
   183     transfer->priv->disposed = TRUE;
   184     
   185   if ( transfer->mutex != NULL ) {
   186   	g_mutex_free( transfer->mutex );
   187   	transfer->mutex = NULL;
   188   }
   189   
   190   if ( transfer->control_sock != NULL ) {
   191   	g_object_unref( transfer->control_sock );
   192   	transfer->control_sock = NULL;
   193   }  
   194   
   195   if ( transfer->sock != NULL ) {
   196   	g_object_unref( transfer->sock );
   197   	transfer->sock = NULL;
   198   }
   199   
   200   if ( transfer->backend_info != NULL ) {
   201   	g_object_unref( transfer->backend_info );
   202   	transfer->backend_info = NULL;
   203   }
   204   
   205   if ( transfer->priv->recorder != NULL ) {
   206   	g_object_unref( transfer->priv->recorder );
   207   	transfer->priv->recorder = NULL;
   208   }
   209   
   210   if ( transfer->filename != NULL ) {
   211   	g_free( transfer->filename );
   212   	transfer->filename = NULL;
   213   }
   214 
   215   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->dispose (object);
   216 }
   217 
   218 static void
   219 gmyth_file_transfer_finalize (GObject *object)
   220 {
   221   g_signal_handlers_destroy (object);
   222 
   223   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->finalize (object);
   224 }
   225 
   226 /** 
   227  * Creates a new instance of GMythFileTransfer.
   228  * 
   229  * @param backend_info The BackendInfo instance, with all the MythTV network 
   230  * 										 configuration data.
   231  * 
   232  * @return a new instance of the File Transfer. 
   233  */
   234 GMythFileTransfer*
   235 gmyth_file_transfer_new (GMythBackendInfo *backend_info)
   236 {
   237   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   238   
   239   transfer->backend_info = backend_info;
   240   g_object_ref (transfer->backend_info);
   241 
   242   return transfer;
   243 }
   244 
   245 /** 
   246  * Creates a new instance of GMythFileTransfer.
   247  * 
   248  * @param uri_str The URI poiting to the MythTV backend server.
   249  * 
   250  * @return a new instance of the File Transfer. 
   251  */
   252 GMythFileTransfer* 
   253 gmyth_file_transfer_new_with_uri (const gchar* uri_str)
   254 {
   255   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   256 
   257   transfer->backend_info = gmyth_backend_info_new_with_uri (uri_str);
   258 
   259   return transfer;
   260 }
   261 
   262 /** 
   263  * Open a File Transfer connection in order to get a remote file.
   264  * 
   265  * @param transfer The actual File Transfer instance. 
   266  * @param filename The file name of the remote file to be transfered to the client.
   267  * 
   268  * @return <code>true</code>, if the connection opening had been done successfully. 
   269  */
   270 gboolean
   271 gmyth_file_transfer_open (GMythFileTransfer *transfer, const gchar* filename)
   272 {
   273   gboolean ret = TRUE;
   274   
   275   g_return_val_if_fail (transfer != NULL, FALSE);
   276   g_return_val_if_fail (filename != NULL && strlen(filename) > 0, FALSE);
   277   
   278   if (transfer->filename != NULL)
   279   {
   280     g_free (transfer->filename);
   281     transfer->filename = NULL;
   282   }
   283 
   284   transfer->filename = g_strdup( filename );
   285   
   286   /* configure the control socket */
   287   if (transfer->control_sock == NULL) { 
   288     if (!gmyth_connect_to_backend (transfer)) {
   289       gmyth_debug ("Connection to backend failed (Control Socket).\n");
   290       ret = FALSE;
   291     }
   292   } else {
   293     g_warning("Remote transfer control socket already created.\n");
   294   }
   295   
   296   gmyth_debug ("Got file with size = %lld.\n", transfer->filesize);
   297   
   298   return ret;
   299 }
   300 
   301 /** 
   302  * Connect a File Transfer binary client socket to a remote file.
   303  * 
   304  * @param transfer The actual File Transfer instance. 
   305  * 
   306  * @return <code>true</code>, if the connection had been configured successfully. 
   307  */
   308 static gboolean
   309 gmyth_connect_to_backend (GMythFileTransfer *transfer)
   310 {
   311   GString *base_str = NULL;
   312   GString *hostname = NULL;
   313   GMythStringList *strlist = NULL; 
   314   gboolean ret = TRUE;
   315 
   316   g_return_val_if_fail (transfer != NULL, FALSE );
   317   
   318   myth_control_acquire_context( transfer, TRUE );
   319 
   320   base_str = g_string_new ("");
   321 
   322   /* Creates the control socket */
   323   
   324   if (transfer->control_sock != NULL) {
   325     g_object_unref (transfer->control_sock);
   326     transfer->control_sock = NULL;
   327   }
   328   
   329   if ( NULL == transfer->control_sock )
   330   {
   331   	transfer->control_sock = gmyth_socket_new();
   332 
   333 	  // Connects the socket, send Mythtv ANN command and verify Mythtv protocol version 
   334 	  if (!gmyth_socket_connect_to_backend (transfer->control_sock,
   335 	        transfer->backend_info->hostname, transfer->backend_info->port, TRUE)) {
   336 	
   337 	    g_object_unref (transfer->control_sock);
   338 	    transfer->control_sock = NULL;
   339 	    return FALSE;
   340 	  }
   341 	  
   342   }
   343     
   344   /* Creates the data socket */
   345   if (transfer->sock != NULL) {
   346     g_object_unref (transfer->sock);
   347     transfer->sock = NULL;
   348   }  
   349   
   350   //if ( NULL == transfer->sock ) {	
   351 	  transfer->sock = gmyth_socket_new ();
   352 	  gmyth_socket_connect (transfer->sock, transfer->backend_info->hostname, transfer->backend_info->port);
   353 	  gmyth_debug("Connecting file transfer... (%s, %d)", transfer->backend_info->hostname, transfer->backend_info->port);	  
   354   //}
   355   
   356   if ( transfer->control_sock != NULL )
   357   {
   358 	
   359 	  strlist = gmyth_string_list_new();
   360 	  hostname = gmyth_socket_get_local_hostname();
   361 	  gmyth_debug( "[%s] MythTV version (from backend) = %d.\n", __FUNCTION__, transfer->control_sock->mythtv_version );
   362 	  if ( transfer->control_sock->mythtv_version > 26 )
   363 	  	g_string_printf( base_str, "ANN FileTransfer %s 1 -1", hostname->str);
   364 	  else
   365 	  	g_string_printf( base_str, "ANN FileTransfer %s", hostname->str);
   366 	
   367 	  gmyth_string_list_append_string (strlist, base_str );
   368 	  gmyth_string_list_append_char_array (strlist, transfer->filename);
   369 	
   370 	  gmyth_socket_write_stringlist (transfer->sock, strlist );
   371 	  gmyth_socket_read_stringlist (transfer->sock, strlist );
   372 	    
   373 	  /* file identification used in future file transfer requests to backend */
   374 	  transfer->file_id = gmyth_string_list_get_int( strlist, 1 );
   375 	
   376 	  /* Myth URI stream file size - decoded using two 8-bytes sequences (64 bits/long long types) */
   377 	  transfer->filesize = gmyth_util_decode_long_long( strlist, 2 );
   378 	
   379 	  gmyth_debug ( "[%s] ***** Received: recordernum = %d, filesize = %" G_GUINT64_FORMAT "\n", __FUNCTION__,
   380 	          transfer->file_id, transfer->filesize );
   381 	
   382 	  if (transfer->filesize < 0 ) {
   383 	      gmyth_debug ( "[%s] Got filesize equals to %llu is lesser than 0 [invalid stream file]\n", __FUNCTION__, transfer->filesize );
   384 	      g_object_unref (transfer->sock);
   385 	      transfer->sock = NULL;
   386 	      ret = FALSE;
   387 	      goto cleanup;
   388 	  }
   389 	  
   390   } /* if - Control Socket is not equals to NULL? */
   391   
   392 cleanup:
   393 
   394 	myth_control_release_context( transfer );
   395 
   396   if ( strlist != NULL )
   397     g_object_unref( strlist );
   398   
   399   if ( base_str != NULL )
   400   	g_string_free (base_str, TRUE);
   401   	
   402   if ( hostname != NULL )
   403   	g_string_free (hostname, TRUE);
   404 
   405   return ret;
   406 }    
   407 
   408 /** 
   409  * Receives a GObject signal coming from a LiveTV instance, all the time a 
   410  * program info changes.
   411  * 
   412  * @param transfer The actual File Transfer instance. 
   413  * @param msg_code The MythTV backend message status code.
   414  * @param live_tv A pointer to the LiveTV instance. * 
   415  */
   416 void
   417 gmyth_file_transfer_emit_program_info_changed_signal ( GMythFileTransfer *transfer, gint msg_code,
   418 			gpointer live_tv_recorder ) 
   419 {
   420 				/*
   421 	g_signal_emit_by_name ( G_OBJECT(transfer),
   422          "program-info-changed",
   423          msg_code, live_tv );
   424          */
   425          
   426   gmyth_debug( "Calling signal handler... [FILE_TRANSFER]" );
   427 
   428   g_signal_emit ( transfer,
   429          GMYTH_FILE_TRANSFER_GET_CLASS (transfer)->program_info_changed_handler_signal_id,
   430          0, /* details */
   431          msg_code, live_tv_recorder );
   432 
   433 }
   434 
   435 /** 
   436  * Checks if the actual File Transfer connection is open.
   437  * 
   438  * @param transfer The actual File Transfer instance. 
   439  * 
   440  * @return <code>true</code>, if the File Transfer connection is opened. 
   441  */
   442 gboolean
   443 gmyth_file_transfer_is_open (GMythFileTransfer *transfer)
   444 {
   445     GMythStringList *strlist;
   446     GString *query;
   447 
   448     g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   449     g_return_val_if_fail (transfer->sock != NULL, FALSE);
   450     
   451     myth_control_acquire_context( transfer, TRUE );
   452 
   453     strlist = gmyth_string_list_new();
   454     query = g_string_new (GMYTHTV_QUERY_HEADER);
   455     g_string_append_printf (query, "%d", transfer->file_id );
   456 
   457     gmyth_string_list_append_string (strlist, query );
   458     gmyth_string_list_append_char_array( strlist, "IS_OPEN" );
   459 
   460     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   461     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   462     
   463     myth_control_release_context( transfer );
   464 
   465     g_string_free (query, TRUE);
   466     g_object_unref (strlist);
   467 
   468     return ( strlist != NULL && gmyth_string_list_get_int( strlist, 0 ) == 1 );
   469 }
   470 
   471 /** 
   472  * Closes a remote File Transfer connection.
   473  * 
   474  * @param transfer The actual File Transfer instance. 
   475  */
   476 void
   477 gmyth_file_transfer_close( GMythFileTransfer *transfer )
   478 {
   479   GMythStringList *strlist;
   480   GString *query;
   481 
   482   g_return_if_fail (transfer->control_sock != NULL);
   483   
   484   myth_control_acquire_context( transfer, TRUE );
   485 
   486   strlist = gmyth_string_list_new( );
   487   query = g_string_new (GMYTHTV_QUERY_HEADER);
   488   g_string_append_printf( query, "%d", transfer->file_id);
   489 
   490   gmyth_string_list_append_string( strlist, query );
   491   gmyth_string_list_append_char_array( strlist, "DONE" );
   492 
   493   if ( gmyth_socket_sendreceive_stringlist(transfer->control_sock, strlist) <= 0 ) {
   494     // fixme: time out???
   495     gmyth_debug ( "Remote file timeout.\n" );
   496   }
   497 
   498   g_string_free (query, TRUE);
   499   g_object_unref (strlist);
   500 
   501   if (transfer->sock) {
   502     g_object_unref( transfer->sock );
   503     transfer->sock = NULL;
   504   }
   505 
   506   if (transfer->control_sock) {
   507     g_object_unref( transfer->control_sock );
   508     transfer->control_sock = NULL;
   509   } 
   510 
   511   myth_control_release_context( transfer );
   512 
   513 }
   514 
   515 /** 
   516  * Do a seek operation (moves the read/write file pointer) on a remote file.
   517  * 
   518  * @param transfer The actual File Transfer instance. 
   519  * @param pos The position to be moved in the remote file.
   520  * @param whence Tells to what direction seek movement should be done.
   521  * 
   522  * @return The actual position on the remote file (after seek has been done). 
   523  */
   524 gint64
   525 gmyth_file_transfer_seek(GMythFileTransfer *transfer, guint64 pos, gint whence)
   526 {
   527   GMythStringList *strlist = gmyth_string_list_new();
   528   GString *query;
   529 
   530   g_return_val_if_fail (transfer->sock != NULL, -1);
   531   g_return_val_if_fail (transfer->control_sock != NULL, -1);
   532 
   533   strlist = gmyth_string_list_new();
   534   query = g_string_new (GMYTHTV_QUERY_HEADER);
   535   g_string_append_printf (query, "%d", transfer->file_id);
   536   
   537   /* myth_control_acquire_context( transfer, TRUE ); */
   538 
   539   gmyth_string_list_append_string( strlist, query );
   540   gmyth_string_list_append_char_array( strlist, "SEEK" );
   541   gmyth_string_list_append_uint64( strlist, pos );
   542   
   543   gmyth_string_list_append_int( strlist, whence );  
   544 
   545   if (pos > 0 )
   546     gmyth_string_list_append_uint64( strlist, pos );
   547   else
   548     gmyth_string_list_append_uint64( strlist, transfer->readposition );
   549   
   550   gmyth_socket_sendreceive_stringlist( transfer->control_sock, strlist );
   551 
   552   gint64 retval = gmyth_string_list_get_int64(strlist, 0);
   553   transfer->readposition = retval;
   554   gmyth_debug ( "[%s] got reading position pointer from the streaming = %lld\n", 
   555       __FUNCTION__, retval );
   556 
   557   g_object_unref (strlist);
   558   g_string_free (query, TRUE);
   559 
   560   /* myth_control_release_context( transfer ); */
   561 
   562   return retval;
   563 }
   564 
   565 /** 
   566  * Acquire access to a remote file socket read/write pointer.
   567  * 
   568  * @param transfer The actual File Transfer instance.
   569  * @param do_wait Waits or not on a GCond, when trying to read from the remote socket.
   570  * 
   571  * @return <code>true</code>, if the acquire had been got. 
   572  */
   573 static gboolean 
   574 myth_control_acquire_context( GMythFileTransfer *transfer, gboolean do_wait )
   575 {
   576 	gboolean ret = TRUE;	
   577 	//guint max_iter = 50;
   578 	
   579 	g_mutex_lock( transfer->mutex );
   580 	
   581   //while ( !has_io_access ) 
   582   //	g_cond_wait( io_watcher_cond, mutex );
   583   	
   584   //has_io_access = FALSE;
   585   
   586   //myth_control_acquire_context (FALSE);
   587    
   588   /* 
   589   if ( do_wait ) {
   590   	while ( --max_iter > 0 && !g_main_context_wait( io_watcher_context, io_watcher_cond, mutex ) )
   591   		ret = FALSE;
   592   } else if ( !g_main_context_acquire( io_watcher_context ) )
   593   	ret = FALSE;
   594   */
   595   	
   596   //g_static_mutex_lock( &st_mutex );
   597   
   598   return ret;
   599 }
   600 
   601 /** 
   602  * Release access to a remote file socket read/write pointer.
   603  * 
   604  * @param transfer The actual File Transfer instance.
   605  * 
   606  * @return <code>true</code>, if the socket read/write permissions had been releaseds. 
   607  */
   608 static gboolean 
   609 myth_control_release_context( GMythFileTransfer *transfer ) 
   610 {
   611 	gboolean ret = TRUE;
   612     
   613   //g_static_mutex_unlock( &st_mutex );
   614   
   615 	//g_main_context_release( io_watcher_context );
   616   
   617   //g_main_context_wakeup( io_watcher_context );
   618   
   619   //has_io_access = TRUE;
   620 
   621   //g_cond_broadcast( io_watcher_cond );
   622   
   623   g_mutex_unlock( transfer->mutex );  
   624  
   625   return ret;
   626 }
   627 
   628 /** 
   629  * Reads a block from a remote file.
   630  * 
   631  * @param transfer The actual File Transfer instance.
   632  * @param data A GByteArray instance, where all the binary data representing 
   633  * 						 the remote file will be stored.
   634  * @param size The block size, in bytes, to be requested from a remote file.
   635  * @param read_unlimited Tells the backend to read indefinitely (LiveTV), or only 
   636  * 											 gets the actual size
   637  * 
   638  * @return The actual block size (in bytes) returned by REQUEST_BLOCK message,
   639  * 				or the error code. 
   640  */
   641 gint 
   642 gmyth_file_transfer_read(GMythFileTransfer *transfer, GByteArray *data, gint size, gboolean read_unlimited)
   643 {
   644   gint bytes_sent = 0;
   645   gsize bytes_read = 0;
   646   gsize total_read = 0;
   647   
   648   GError *error = NULL;
   649   
   650   GIOChannel *io_channel;
   651   GIOChannel *io_channel_control;
   652 
   653   GIOCondition io_cond;
   654   GIOCondition io_cond_control;
   655   GIOStatus io_status = G_IO_STATUS_NORMAL, io_status_control = G_IO_STATUS_NORMAL;
   656   
   657   gboolean ret = TRUE;
   658 
   659   GString *query;
   660   
   661   g_return_val_if_fail ( data != NULL, -2 );
   662 
   663   io_channel = transfer->sock->sd_io_ch;
   664   io_channel_control = transfer->control_sock->sd_io_ch;
   665 
   666   io_status = g_io_channel_set_encoding( io_channel, NULL, &error );
   667   if ( io_status == G_IO_STATUS_NORMAL )
   668     gmyth_debug ( "[%s] Setting encoding to binary data socket).\n", __FUNCTION__ );
   669 
   670   io_cond = g_io_channel_get_buffer_condition( io_channel );  
   671 
   672   io_cond_control = g_io_channel_get_buffer_condition( io_channel );
   673   if ( transfer->sock == NULL || ( io_status == G_IO_STATUS_ERROR ) ) {
   674     g_printerr( "gmyth_file_transfer_read(): Called with no raw socket.\n" );
   675     return -1;
   676   }
   677 
   678   if ( transfer->control_sock == NULL || ( io_status_control == G_IO_STATUS_ERROR ) ) {
   679     g_printerr( "gmyth_file_transfer_read(): Called with no control socket.\n" );
   680     return -1;
   681   }
   682 
   683   query = g_string_new (GMYTHTV_QUERY_HEADER);
   684   g_string_append_printf ( query, "%d", transfer->file_id );
   685   gmyth_debug ("[%s] Transfer_query = %s\n", __FUNCTION__, query->str );
   686   
   687   /* send requests to the maximum number of REQUEST_BLOCK messages */
   688   guint max_tries = 5;
   689   
   690   myth_control_acquire_context( transfer, TRUE );
   691   
   692   while (total_read == 0 && --max_tries > 0) {
   693     GMythStringList *strlist = gmyth_string_list_new();
   694 
   695     gmyth_string_list_append_char_array( strlist, query->str );
   696     gmyth_string_list_append_char_array( strlist, "REQUEST_BLOCK" );
   697     gmyth_string_list_append_int( strlist, size );
   698 
   699     // Request the block to the backend
   700     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   701 
   702     // Receives the backand answer    
   703     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   704 
   705     if ( strlist != NULL && gmyth_string_list_length( strlist ) > 0 ) 
   706     { 
   707 	    bytes_sent = gmyth_string_list_get_int( strlist,  0 ); // -1 on backend error
   708 	    gmyth_debug ( "[%s] got SENT buffer message = %d\n", __FUNCTION__, bytes_sent );
   709 
   710     	if ( bytes_sent > 0 )
   711     	{
   712         gchar *data_buffer = g_new0 ( gchar, bytes_sent );    
   713    	    while ( 0 != bytes_sent ) 
   714    	    { 
   715           io_status = g_io_channel_read_chars( io_channel, data_buffer + total_read, 
   716 	    				(gsize) bytes_sent,	&bytes_read, &error );
   717 
   718 	        total_read += bytes_read;
   719           bytes_sent -= bytes_read;
   720 
   721             /* append new data to the increasing byte array */
   722 	    		data = g_byte_array_append (data, (const guint8*)data_buffer, bytes_read);
   723 	        gmyth_debug ("Total transfer data read: %d\n", total_read);
   724         }
   725         g_free (data_buffer);
   726       } else if ( bytes_sent <= 0 && !read_unlimited ) {
   727       	total_read = GMYTHTV_FILE_TRANSFER_READ_ERROR;
   728     		g_object_unref (strlist);
   729     		strlist = NULL;
   730     		break;
   731     	}
   732     } else if ( !read_unlimited || !(transfer->priv != NULL && transfer->priv->recorder != NULL &&
   733 					transfer->priv->do_next_program_chain) ) {
   734     	total_read = GMYTHTV_FILE_TRANSFER_READ_ERROR;
   735     	g_object_unref (strlist);
   736     	strlist = NULL;
   737     	break;
   738     }
   739     if ( strlist!=NULL )
   740     {
   741 	    g_object_unref (strlist);
   742 	    strlist = NULL;
   743     }
   744   } /* while - iterates through bytes until reaches the end of stream */
   745 
   746   if ( read_unlimited && ( bytes_sent == 0 ) && ( max_tries == 0 ) )
   747 	{
   748 		gmyth_debug( "Trying to move to the next program chain..." );
   749 		transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);
   750 		
   751 		if ( transfer->priv != NULL && transfer->priv->recorder != NULL &&
   752 					transfer->priv->do_next_program_chain )
   753 		{
   754 		
   755 			  total_read = GMYTHTV_FILE_TRANSFER_NEXT_PROG_CHAIN;	
   756 		
   757 	  		//g_mutex_lock( transfer->mutex );
   758 	  		
   759 	  		//ret = gmyth_livetv_next_program_chain( transfer->priv->recorder );
   760 	  		GMythProgramInfo *prog_info = gmyth_recorder_get_current_program_info( transfer->priv->recorder );
   761 	  		
   762 	  		if ( prog_info != NULL && prog_info->pathname != NULL && strlen( prog_info->pathname->str ) > 0 &&
   763 	  						g_ascii_strcasecmp( prog_info->pathname->str, transfer->filename ) != 0 )
   764 	  			ret = gmyth_file_transfer_open ( transfer, g_strrstr( prog_info->pathname->str, "/" ) );
   765 	  		
   766 	  		if ( prog_info != NULL )
   767 	  			g_object_unref( prog_info );
   768 	  			
   769 	  		//g_mutex_unlock( transfer->mutex );
   770   		
   771 			if ( !ret )
   772 				gmyth_debug( "Cannot change to the next program info!" );
   773 			else
   774 				gmyth_debug( "OK!!! MOVED to the next program info [%s]!", 
   775 										transfer->filename );						
   776 		}
   777 
   778 	} /* if */
   779   
   780   myth_control_release_context( transfer );
   781   g_string_free (query, TRUE);
   782 
   783   if ( error != NULL ) {
   784     gmyth_debug ("Cleaning-up ERROR: %s [msg = %s, code = %d]\n", __FUNCTION__, error->message, 
   785 	    error->code);
   786     g_error_free (error);
   787   }
   788   
   789   if ( total_read > 0 )
   790   	transfer->readposition += total_read;  	
   791   	
   792   return total_read;
   793 }
   794 
   795 
   796 static void 
   797 gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   798 										gint msg_code, gpointer livetv_recorder )
   799 {
   800 	GMythRecorder *recorder = GMYTH_RECORDER( livetv_recorder );
   801 	
   802 	gmyth_debug( "Program info changed! ( file transfer orig. = %p, ptr. = [%s], user data = [%s] )", transfer, 
   803 		livetv_recorder != NULL ? "[NOT NULL]" : "[NULL]", livetv != NULL ? "[NOT NULL]" : "[NULL]" );
   804 	
   805 	if ( NULL != recorder )
   806 	{
   807 		gmyth_debug( "YES, the requested program info movement on the LiveTV transfer is authentical!" );
   808 	}
   809 	
   810 	transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   811     
   812 	g_object_ref(recorder);
   813 	
   814 	transfer->priv->recorder = recorder;
   815 
   816 	transfer->priv->do_next_program_chain = TRUE;
   817 }
   818 
   819 /** 
   820  * Sets the timeout flag of a file being transfered.
   821  * 
   822  * @param transfer The actual File Transfer instance.
   823  * @param fast If this is <code>true</code>, sets the remote timeout to be as fast
   824  * 						as possible.
   825  * 
   826  * @return <code>true</code>, if the acquire had been got. 
   827  */
   828 gboolean 
   829 gmyth_file_transfer_settimeout( GMythFileTransfer *transfer, gboolean fast )
   830 {
   831 
   832   GMythStringList *strlist = NULL;
   833 
   834   g_return_val_if_fail (transfer->sock != NULL, FALSE);
   835   g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   836   
   837   myth_control_acquire_context( transfer, TRUE );
   838 
   839   strlist = gmyth_string_list_new(); 
   840   gmyth_string_list_append_char_array( strlist, GMYTHTV_QUERY_HEADER );
   841   gmyth_string_list_append_char_array( strlist, "SET_TIMEOUT" );
   842   gmyth_string_list_append_int( strlist, fast ); 
   843 
   844   gint strlist_len = gmyth_socket_sendreceive_stringlist( transfer->control_sock, 
   845   				strlist );
   846   
   847   if ( strlist_len > 0 )
   848   	gmyth_debug( "Yes, timeout was changed: %s.", gmyth_string_list_get_char_array( strlist, 0 ) );
   849   else
   850   	gmyth_debug( "Timeout cannot be changed!" );
   851   	
   852   myth_control_release_context( transfer );
   853 
   854   gmyth_debug ( "%s setting timeout flag of this file transfer = %s\n", 
   855   		strlist_len > 0 ? "Yes," : "NOT", fast ? "FAST" : "NOT FAST" );
   856 
   857   g_object_unref (strlist);
   858 
   859   return TRUE;
   860 }
   861 
   862 /** 
   863  * Gets the actual file size of the binary content.
   864  * 
   865  * @param transfer The actual File Transfer instance.
   866  * 
   867  * @return The actual file size in bytes. 
   868  */
   869 guint64
   870 gmyth_file_transfer_get_filesize (GMythFileTransfer *transfer)
   871 {
   872     g_return_val_if_fail (transfer != NULL, 0);
   873     return transfer->filesize;
   874 }