gmyth/src/gmyth_file_transfer.c
author rosfran
Thu Mar 08 19:32:21 2007 +0000 (2007-03-08)
branchtrunk
changeset 404 c057ae06c14b
parent 384 060c3ceb43a0
child 406 131040a49e9f
permissions -rwxr-xr-x
[svn r409] Fixes read function from GMythFileTransfer to not try to read continuously if it was not LiveTV.
     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_livetv.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 	
    93 	GMythLiveTV *livetv;
    94 	
    95 	gboolean do_next_program_chain;
    96 	
    97 };
    98 
    99 static void gmyth_file_transfer_class_init          (GMythFileTransferClass *klass);
   100 static void gmyth_file_transfer_init                (GMythFileTransfer *object);
   101 
   102 static void gmyth_file_transfer_dispose  (GObject *object);
   103 static void gmyth_file_transfer_finalize (GObject *object);
   104 
   105 static void gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   106 										gint msg_code, gpointer livetv_transfer );
   107 
   108 static gboolean gmyth_connect_to_backend (GMythFileTransfer *transfer);
   109 
   110 void gmyth_file_transfer_close( GMythFileTransfer *transfer );
   111 
   112 static gboolean myth_control_acquire_context( GMythFileTransfer *transfer, gboolean do_wait );
   113 
   114 static gboolean myth_control_release_context( GMythFileTransfer *transfer );
   115 
   116 G_DEFINE_TYPE(GMythFileTransfer, gmyth_file_transfer, G_TYPE_OBJECT)
   117 
   118 static void
   119 gmyth_file_transfer_class_init (GMythFileTransferClass *klass)
   120 {
   121   GObjectClass *gobject_class;
   122   GMythFileTransferClass *gtransfer_class;
   123 
   124   gobject_class = (GObjectClass *) klass;
   125   gtransfer_class = (GMythFileTransferClass *) gobject_class;
   126 
   127   gobject_class->dispose  = gmyth_file_transfer_dispose;
   128   gobject_class->finalize = gmyth_file_transfer_finalize;
   129   
   130   g_type_class_add_private (gobject_class, sizeof (GMythFileTransferPrivate));
   131   
   132 	gtransfer_class->program_info_changed_handler = gmyth_file_transfer_program_info_changed;
   133   
   134 	gtransfer_class->program_info_changed_handler_signal_id = 
   135 		  g_signal_new ( "program-info-changed",
   136 		                 G_TYPE_FROM_CLASS (gtransfer_class),
   137 		                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
   138 		                 0,
   139 										 NULL,
   140 										 NULL,
   141 										 gmyth_marshal_VOID__INT_POINTER,
   142 										 G_TYPE_NONE,
   143 										 2,
   144 										 G_TYPE_INT,
   145 										 G_TYPE_POINTER );
   146 
   147 }
   148 
   149 static void
   150 gmyth_file_transfer_init (GMythFileTransfer *transfer)
   151 { 
   152   g_return_if_fail( transfer != NULL );
   153 
   154   transfer->readposition = 0;
   155   transfer->filesize = GMYTHTV_FILE_SIZE;
   156   
   157   transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   158   
   159   transfer->priv->do_next_program_chain = FALSE;
   160   transfer->priv->livetv = NULL;
   161 
   162   transfer->control_sock = NULL;
   163   transfer->sock = NULL;
   164   
   165   transfer->mutex = g_mutex_new();
   166   
   167 	g_signal_connect ( G_OBJECT (transfer), "program-info-changed",
   168               (GCallback)(GMYTH_FILE_TRANSFER_GET_CLASS(transfer)->program_info_changed_handler),
   169               NULL );
   170 
   171 }
   172 
   173 static void
   174 gmyth_file_transfer_dispose  (GObject *object)
   175 {	
   176 	GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (object);
   177 	
   178   if ( transfer->mutex != NULL )  
   179   {
   180   	g_mutex_free( transfer->mutex );
   181   	transfer->mutex = NULL;
   182   }
   183   
   184   if ( transfer->control_sock != NULL )  
   185   {
   186   	g_object_unref( transfer->control_sock );
   187   	transfer->control_sock = NULL;
   188   }  
   189   
   190   if ( transfer->sock != NULL )  
   191   {
   192   	g_object_unref( transfer->sock );
   193   	transfer->sock = NULL;
   194   }
   195   
   196   if ( transfer->priv->livetv != NULL )  
   197   {
   198   	g_object_unref( transfer->priv->livetv );
   199   	transfer->priv->livetv = NULL;
   200   }
   201 
   202 	/*  
   203   if ( transfer->priv != NULL )  
   204   {
   205   	g_object_unref( transfer->priv );
   206   	transfer->priv = NULL;
   207   }
   208   */
   209     
   210   if ( transfer->filename != NULL )
   211   {
   212   	g_free( transfer->filename );
   213   	transfer->filename = NULL;
   214   }
   215 
   216   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->dispose (object);
   217 }
   218 
   219 static void
   220 gmyth_file_transfer_finalize (GObject *object)
   221 {
   222   g_signal_handlers_destroy (object);
   223 
   224   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->finalize (object);
   225 }
   226 
   227 // fixme: do we need the card_id????
   228 GMythFileTransfer*
   229 gmyth_file_transfer_new ( const GMythBackendInfo *backend_info)
   230 {
   231   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   232   
   233   transfer->backend_info = (GMythBackendInfo *)backend_info;
   234   g_object_ref (transfer->backend_info);
   235 
   236   return transfer;
   237 }
   238 
   239 GMythFileTransfer* 
   240 gmyth_file_transfer_new_with_uri (const gchar* uri_str)
   241 {
   242   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   243 
   244   transfer->backend_info = gmyth_backend_info_new_with_uri (uri_str);
   245 
   246   return transfer;
   247 }
   248 
   249 gboolean
   250 gmyth_file_transfer_open (GMythFileTransfer *transfer, const gchar* filename)
   251 {
   252   gboolean ret = TRUE;
   253   
   254   g_return_val_if_fail (transfer != NULL, FALSE);
   255   g_return_val_if_fail (filename != NULL && strlen(filename) > 0, FALSE);
   256   /*
   257   
   258   if (transfer->filename != NULL)
   259   {
   260     g_free (transfer->filename);
   261     transfer->filename = NULL;
   262   }
   263   */
   264 
   265   transfer->filename = g_strdup( filename );
   266   
   267   /* configure the control socket */
   268   if (transfer->control_sock == NULL) { 
   269     if (!gmyth_connect_to_backend (transfer)) {
   270       gmyth_debug ("Connection to backend failed (Control Socket).\n");
   271       ret = FALSE;
   272     }
   273   
   274   } else {
   275     g_warning("Remote transfer control socket already created.\n");
   276   }
   277   
   278   gmyth_debug ("Got file with size = %lld.\n", transfer->filesize);
   279   
   280   return ret;
   281 
   282 }
   283 
   284 static gboolean
   285 gmyth_connect_to_backend (GMythFileTransfer *transfer)
   286 {
   287   GString *base_str = NULL;
   288   GString *hostname = NULL;
   289   GMythStringList *strlist = NULL; 
   290   gboolean ret = TRUE;
   291 
   292   g_return_val_if_fail (transfer != NULL, FALSE );
   293   
   294   myth_control_acquire_context( transfer, TRUE );
   295 
   296   base_str = g_string_new ("");
   297 
   298   /* Creates the control socket */
   299   
   300   if (transfer->control_sock != NULL) {
   301     g_object_unref (transfer->control_sock);
   302     transfer->control_sock = NULL;
   303   }
   304   
   305   //if ( NULL == transfer->control_sock )
   306   //{
   307   	transfer->control_sock = gmyth_socket_new();
   308 
   309 	  // Connects the socket, send Mythtv ANN command and verify Mythtv protocol version 
   310 	  if (!gmyth_socket_connect_to_backend (transfer->control_sock,
   311 	        transfer->backend_info->hostname, transfer->backend_info->port, TRUE)) {
   312 	
   313 	    g_object_unref (transfer->control_sock);
   314 	    transfer->control_sock = NULL;
   315 	    return FALSE;
   316 	  }
   317 	  
   318   //}
   319     
   320   /* Creates the data socket */
   321   if (transfer->sock != NULL) {
   322     g_object_unref (transfer->sock);
   323     transfer->sock = NULL;
   324   }  
   325   
   326   //if ( NULL == transfer->sock ) {	
   327 	  transfer->sock = gmyth_socket_new ();
   328 	  gmyth_socket_connect (transfer->sock, transfer->backend_info->hostname, transfer->backend_info->port);
   329 	  gmyth_debug("Connecting file transfer... (%s, %d)", transfer->backend_info->hostname, transfer->backend_info->port);	  
   330   //}
   331   
   332   if ( transfer->control_sock != NULL )
   333   {
   334 	
   335 	  strlist = gmyth_string_list_new();
   336 	  hostname = gmyth_socket_get_local_hostname();
   337 	  gmyth_debug( "[%s] MythTV version (from backend) = %d.\n", __FUNCTION__, transfer->control_sock->mythtv_version );
   338 	  if ( transfer->control_sock->mythtv_version > 26 )
   339 	  	g_string_printf( base_str, "ANN FileTransfer %s 1 -1", hostname->str);
   340 	  else
   341 	  	g_string_printf( base_str, "ANN FileTransfer %s", hostname->str);
   342 	
   343 	  gmyth_string_list_append_string (strlist, base_str );
   344 	  gmyth_string_list_append_char_array (strlist, transfer->filename);
   345 	
   346 	  gmyth_socket_write_stringlist (transfer->sock, strlist );
   347 	  gmyth_socket_read_stringlist (transfer->sock, strlist );
   348 	    
   349 	  /* file identification used in future file transfer requests to backend */
   350 	  transfer->file_id = gmyth_string_list_get_int( strlist, 1 );
   351 	
   352 	  /* Myth URI stream file size - decoded using two 8-bytes sequences (64 bits/long long types) */
   353 	  transfer->filesize = gmyth_util_decode_long_long( strlist, 2 );
   354 	
   355 	  gmyth_debug ( "[%s] ***** Received: recordernum = %d, filesize = %" G_GUINT64_FORMAT "\n", __FUNCTION__,
   356 	          transfer->file_id, transfer->filesize );
   357 	
   358 	  if (transfer->filesize < 0 ) {
   359 	      gmyth_debug ( "[%s] Got filesize equals to %llu is lesser than 0 [invalid stream file]\n", __FUNCTION__, transfer->filesize );
   360 	      g_object_unref (transfer->sock);
   361 	      transfer->sock = NULL;
   362 	      ret = FALSE;
   363 	      goto cleanup;
   364 	  }
   365 	  
   366   } /* if - Control Socket is not equals to NULL? */
   367   
   368 cleanup:
   369 
   370 	myth_control_release_context( transfer );
   371 
   372   if ( strlist != NULL )
   373     g_object_unref( strlist );
   374   
   375   if ( base_str != NULL )
   376   	g_string_free (base_str, TRUE);
   377   	
   378   if ( hostname != NULL )
   379   	g_string_free (hostname, TRUE);
   380 
   381   return ret;
   382 }    
   383 
   384 void
   385 gmyth_file_transfer_emit_program_info_changed_signal ( GMythFileTransfer *transfer, gint msg_code,
   386 			gpointer live_tv ) 
   387 {
   388 				/*
   389 	g_signal_emit_by_name ( G_OBJECT(transfer),
   390          "program-info-changed",
   391          msg_code, live_tv );
   392          */
   393          
   394   gmyth_debug( "Calling signal handler... [FILE_TRANSFER]" );
   395 
   396   g_signal_emit ( transfer,
   397          GMYTH_FILE_TRANSFER_GET_CLASS (transfer)->program_info_changed_handler_signal_id,
   398          0, /* details */
   399          msg_code, live_tv );
   400 
   401 }
   402 
   403 gboolean
   404 gmyth_file_transfer_is_open (GMythFileTransfer *transfer)
   405 {
   406     GMythStringList *strlist;
   407     GString *query;
   408 
   409     g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   410     g_return_val_if_fail (transfer->sock != NULL, FALSE);
   411     
   412     myth_control_acquire_context( transfer, TRUE );
   413 
   414     strlist = gmyth_string_list_new();
   415     query = g_string_new (GMYTHTV_QUERY_HEADER);
   416     g_string_append_printf (query, "%d", transfer->file_id );
   417 
   418     gmyth_string_list_append_string (strlist, query );
   419     gmyth_string_list_append_char_array( strlist, "IS_OPEN" );
   420 
   421     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   422     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   423     
   424     myth_control_release_context( transfer );
   425 
   426     g_string_free (query, TRUE);
   427     g_object_unref (strlist);
   428 
   429     return ( strlist != NULL && gmyth_string_list_get_int( strlist, 0 ) == 1 );
   430 }
   431 
   432 void
   433 gmyth_file_transfer_close( GMythFileTransfer *transfer )
   434 {
   435   GMythStringList *strlist;
   436   GString *query;
   437 
   438   g_return_if_fail (transfer->control_sock != NULL);
   439   
   440   myth_control_acquire_context( transfer, TRUE );
   441 
   442   strlist = gmyth_string_list_new( );
   443   query = g_string_new (GMYTHTV_QUERY_HEADER);
   444   g_string_append_printf( query, "%d", transfer->file_id);
   445 
   446   gmyth_string_list_append_string( strlist, query );
   447   gmyth_string_list_append_char_array( strlist, "DONE" );
   448 
   449   if ( gmyth_socket_sendreceive_stringlist(transfer->control_sock, strlist) <= 0 ) {
   450     // fixme: time out???
   451     gmyth_debug ( "Remote file timeout.\n" );
   452   }
   453 
   454   g_string_free (query, TRUE);
   455   g_object_unref (strlist);
   456 
   457   if (transfer->sock) {
   458     g_object_unref( transfer->sock );
   459     transfer->sock = NULL;
   460   }
   461 
   462   if (transfer->control_sock) {
   463     g_object_unref( transfer->control_sock );
   464     transfer->control_sock = NULL;
   465   } 
   466 
   467   myth_control_release_context( transfer );
   468 
   469 }
   470 
   471 gint64
   472 gmyth_file_transfer_seek(GMythFileTransfer *transfer, guint64 pos, gint whence)
   473 {
   474   GMythStringList *strlist = gmyth_string_list_new();
   475   GString *query;
   476 
   477   g_return_val_if_fail (transfer->sock != NULL, -1);
   478   g_return_val_if_fail (transfer->control_sock != NULL, -1);
   479 
   480   strlist = gmyth_string_list_new();
   481   query = g_string_new (GMYTHTV_QUERY_HEADER);
   482   g_string_append_printf (query, "%d", transfer->file_id);
   483   
   484   /* myth_control_acquire_context( transfer, TRUE ); */
   485 
   486   gmyth_string_list_append_string( strlist, query );
   487   gmyth_string_list_append_char_array( strlist, "SEEK" );
   488   gmyth_string_list_append_uint64( strlist, pos );
   489   
   490   gmyth_string_list_append_int( strlist, whence );  
   491 
   492   if (pos > 0 )
   493     gmyth_string_list_append_uint64( strlist, pos );
   494   else
   495     gmyth_string_list_append_uint64( strlist, transfer->readposition );
   496   
   497   gmyth_socket_sendreceive_stringlist( transfer->control_sock, strlist );
   498 
   499   gint64 retval = gmyth_string_list_get_int64(strlist, 0);
   500   transfer->readposition = retval;
   501   gmyth_debug ( "[%s] got reading position pointer from the streaming = %lld\n", 
   502       __FUNCTION__, retval );
   503 
   504   /* myth_control_release_context( transfer ); */
   505 
   506   return retval;
   507 }
   508 
   509 static gboolean 
   510 myth_control_acquire_context( GMythFileTransfer *transfer, gboolean do_wait )
   511 {
   512 	gboolean ret = TRUE;	
   513 	//guint max_iter = 50;
   514 	
   515 	g_mutex_lock( transfer->mutex );
   516 	
   517   //while ( !has_io_access ) 
   518   //	g_cond_wait( io_watcher_cond, mutex );
   519   	
   520   //has_io_access = FALSE;
   521   
   522   //myth_control_acquire_context (FALSE);
   523    
   524   /* 
   525   if ( do_wait ) {
   526   	while ( --max_iter > 0 && !g_main_context_wait( io_watcher_context, io_watcher_cond, mutex ) )
   527   		ret = FALSE;
   528   } else if ( !g_main_context_acquire( io_watcher_context ) )
   529   	ret = FALSE;
   530   */
   531   	
   532   //g_static_mutex_lock( &st_mutex );
   533   
   534   return ret;
   535 }
   536 
   537 static gboolean 
   538 myth_control_release_context( GMythFileTransfer *transfer ) 
   539 {
   540 	gboolean ret = TRUE;
   541     
   542   //g_static_mutex_unlock( &st_mutex );
   543   
   544 	//g_main_context_release( io_watcher_context );
   545   
   546   //g_main_context_wakeup( io_watcher_context );
   547   
   548   //has_io_access = TRUE;
   549 
   550   //g_cond_broadcast( io_watcher_cond );
   551   
   552   g_mutex_unlock( transfer->mutex );  
   553  
   554   return ret;
   555 }
   556 
   557 gint 
   558 gmyth_file_transfer_read(GMythFileTransfer *transfer, GByteArray *data, gint size, gboolean read_unlimited)
   559 {
   560   gint bytes_sent = 0;
   561   gsize bytes_read = 0;
   562   gsize total_read = 0;
   563   
   564   GError *error = NULL;
   565   
   566   GIOChannel *io_channel;
   567   GIOChannel *io_channel_control;
   568 
   569   GIOCondition io_cond;
   570   GIOCondition io_cond_control;
   571   GIOStatus io_status = G_IO_STATUS_NORMAL, io_status_control = G_IO_STATUS_NORMAL;
   572   
   573   gboolean ret = TRUE;   
   574 
   575   GString *query;
   576   
   577   g_return_val_if_fail ( data != NULL, -2 );
   578 
   579   io_channel = transfer->sock->sd_io_ch;
   580   io_channel_control = transfer->control_sock->sd_io_ch;
   581 
   582   io_status = g_io_channel_set_encoding( io_channel, NULL, &error );
   583   if ( io_status == G_IO_STATUS_NORMAL )
   584     gmyth_debug ( "[%s] Setting encoding to binary data socket).\n", __FUNCTION__ );
   585 
   586   io_cond = g_io_channel_get_buffer_condition( io_channel );  
   587 
   588   io_cond_control = g_io_channel_get_buffer_condition( io_channel );
   589   if ( transfer->sock == NULL || ( io_status == G_IO_STATUS_ERROR ) ) {
   590     g_printerr( "gmyth_file_transfer_read(): Called with no raw socket.\n" );
   591     return -1;
   592   }
   593 
   594   if ( transfer->control_sock == NULL || ( io_status_control == G_IO_STATUS_ERROR ) ) {
   595     g_printerr( "gmyth_file_transfer_read(): Called with no control socket.\n" );
   596     return -1;
   597   }
   598 
   599   query = g_string_new (GMYTHTV_QUERY_HEADER);
   600   g_string_append_printf ( query, "%d", transfer->file_id );
   601   gmyth_debug ("[%s] Transfer_query = %s\n", __FUNCTION__, query->str );
   602   
   603   /* send requests to the maximum number of REQUEST_BLOCK messages */
   604   guint max_tries = 5;
   605   
   606   myth_control_acquire_context( transfer, TRUE );
   607   
   608   while (total_read == 0 && --max_tries > 0) {
   609     GMythStringList *strlist = gmyth_string_list_new();
   610 
   611     gmyth_string_list_append_char_array( strlist, query->str );
   612     gmyth_string_list_append_char_array( strlist, "REQUEST_BLOCK" );
   613     gmyth_string_list_append_int( strlist, size );
   614 
   615     // Request the block to the backend
   616     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   617 
   618     // Receives the backand answer    
   619     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   620 
   621     if ( strlist != NULL && gmyth_string_list_length( strlist ) > 0 ) 
   622     { 
   623 	    bytes_sent = gmyth_string_list_get_int( strlist,  0 ); // -1 on backend error
   624 	    gmyth_debug ( "[%s] got SENT buffer message = %d\n", __FUNCTION__, bytes_sent );
   625 
   626     	if ( bytes_sent >= 0 )
   627     	{
   628         gchar *data_buffer = g_new0 ( gchar, bytes_sent );    
   629    	    while ( 0 != bytes_sent ) 
   630    	    { 
   631           io_status = g_io_channel_read_chars( io_channel, data_buffer + total_read, 
   632 	    				(gsize) bytes_sent,	&bytes_read, &error );
   633 
   634 	        total_read += bytes_read;
   635           bytes_sent -= bytes_read;
   636 
   637             /* append new data to the increasing byte array */
   638 	    		data = g_byte_array_append (data, (const guint8*)data_buffer, bytes_read);
   639 	        gmyth_debug ("Total transfer data read: %d\n", total_read);
   640         }
   641         g_free (data_buffer);
   642       } /* if */
   643     } else if ( !read_unlimited || !(transfer->priv != NULL && transfer->priv->livetv != NULL &&
   644 					transfer->priv->do_next_program_chain) ) {
   645     	total_read = GMYTHTV_FILE_TRANSFER_READ_ERROR;
   646     	g_object_unref (strlist);
   647     	strlist = NULL;
   648     	break;
   649     }
   650     if ( strlist!=NULL )
   651     {
   652 	    g_object_unref (strlist);
   653 	    strlist = NULL;
   654     }
   655   } /* while - iterates through bytes until reaches the end of stream */
   656 
   657   if ( bytes_sent == 0 && max_tries == 0 )
   658 	{
   659 		gmyth_debug( "Trying to move to the next program chain..." );
   660 		transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);
   661 		
   662 		if ( transfer->priv != NULL && transfer->priv->livetv != NULL &&
   663 					transfer->priv->do_next_program_chain )
   664 		{
   665 		
   666 			  total_read = GMYTHTV_FILE_TRANSFER_NEXT_PROG_CHAIN;	
   667 		
   668 	  		//g_mutex_lock( transfer->mutex );
   669 	  		
   670 	  		//ret = gmyth_livetv_next_program_chain( transfer->priv->livetv );
   671 	  		GMythProgramInfo *prog_info = gmyth_recorder_get_current_program_info( transfer->priv->livetv->recorder );
   672 	  		
   673 	  		if ( prog_info != NULL && prog_info->pathname != NULL && strlen( prog_info->pathname->str ) > 0 &&
   674 	  						g_ascii_strcasecmp( prog_info->pathname->str, transfer->filename ) != 0 )
   675 	  			ret = gmyth_file_transfer_open ( transfer, g_strrstr( prog_info->pathname->str, "/" ) );
   676 	  		
   677 	  		if ( prog_info != NULL )
   678 	  			g_object_unref( prog_info );
   679 	  			
   680 	  		//g_mutex_unlock( transfer->mutex );
   681   		
   682 			if ( !ret )
   683 				gmyth_debug( "Cannot change to the next program info!" );
   684 			else
   685 				gmyth_debug( "OK!!! MOVED to the next program info [%s]!", 
   686 										transfer->filename );						
   687 		}
   688 
   689 	} /* if */
   690   
   691   myth_control_release_context( transfer );
   692   g_string_free (query, TRUE);
   693 
   694   if ( error != NULL ) {
   695     gmyth_debug ("Cleaning-up ERROR: %s [msg = %s, code = %d]\n", __FUNCTION__, error->message, 
   696 	    error->code);
   697     g_error_free (error);
   698   }
   699   
   700   if ( total_read > 0 )
   701   	transfer->readposition += total_read;  	
   702   	
   703   return total_read;
   704 }
   705 
   706 static void 
   707 gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   708 										gint msg_code, gpointer livetv_transfer )
   709 {
   710 	GMythLiveTV *livetv = GMYTH_LIVETV( livetv_transfer );
   711 	
   712 	gmyth_debug( "Program info changed! ( file transfer orig. = %p, ptr. = [%s], user data = [%s] )", transfer, 
   713 		livetv_transfer != NULL ? "[NOT NULL]" : "[NULL]", livetv != NULL ? "[NOT NULL]" : "[NULL]" );
   714 	
   715 	if ( livetv != NULL && transfer == livetv->file_transfer )
   716 	{
   717 		gmyth_debug( "YES, the requested program info movement on the LiveTV transfer is authentical!" );
   718 	}
   719 	
   720 	transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   721 	
   722 	transfer->priv->livetv = livetv;
   723 	transfer->priv->do_next_program_chain = TRUE;
   724 	
   725 }
   726 
   727 gboolean 
   728 gmyth_file_transfer_settimeout( GMythFileTransfer *transfer, gboolean fast )
   729 {
   730 
   731   GMythStringList *strlist = NULL;
   732 
   733   g_return_val_if_fail (transfer->sock != NULL, FALSE);
   734   g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   735   
   736   myth_control_acquire_context( transfer, TRUE );
   737 
   738   strlist = gmyth_string_list_new(); 
   739   gmyth_string_list_append_char_array( strlist, GMYTHTV_QUERY_HEADER );
   740   gmyth_string_list_append_char_array( strlist, "SET_TIMEOUT" );
   741   gmyth_string_list_append_int( strlist, fast ); 
   742 
   743   gint strlist_len = gmyth_socket_sendreceive_stringlist( transfer->control_sock, 
   744   				strlist );
   745   
   746   if ( strlist_len > 0 )
   747   	gmyth_debug( "Yes, timeout was changed: %s.", gmyth_string_list_get_char_array( strlist, 0 ) );
   748   else
   749   	gmyth_debug( "Timeout cannot be changed!" );
   750   	
   751  	myth_control_release_context( transfer );
   752 
   753   gmyth_debug ( "%s setting timeout flag of this file transfer = %s\n", 
   754   		strlist_len > 0 ? "Yes," : "NOT", fast ? "FAST" : "NOT FAST" );
   755 
   756   return TRUE;
   757 }
   758 
   759 guint64
   760 gmyth_file_transfer_get_filesize (GMythFileTransfer *transfer)
   761 {
   762     g_return_val_if_fail (transfer != NULL, 0);
   763     return transfer->filesize;
   764 }