gmyth/src/gmyth_file_transfer.c
author rosfran
Fri Dec 15 00:21:57 2006 +0000 (2006-12-15)
branchtrunk
changeset 221 b250ee0438a2
parent 220 462a3c81abd6
child 232 00b55a87f1ba
permissions -rwxr-xr-x
[svn r222] Some fixes to the GTimeVal, non-conformant g_time_val (ISO 8601) functions.
     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					8*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, gpointer user_data );
   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( gboolean do_wait );
   113 
   114 static gboolean myth_control_release_context( );
   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_signal_id = 
   133 		  g_signal_new ("program-info-changed",
   134 		                 G_TYPE_FROM_CLASS (gtransfer_class),
   135 		                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
   136 		                 0,
   137 										 NULL,
   138 										 NULL,
   139 										 gmyth_marshal_VOID__INT_POINTER,
   140 										 G_TYPE_NONE,
   141 										 2,
   142 										 G_TYPE_INT,
   143 										 G_TYPE_POINTER);
   144 										 
   145 	gtransfer_class->program_info_changed_handler = gmyth_file_transfer_program_info_changed; 
   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 }
   168 
   169 static void
   170 gmyth_file_transfer_dispose  (GObject *object)
   171 {
   172 	GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (object);
   173 	
   174   if ( transfer->mutex != NULL )  
   175   {
   176   	g_mutex_free( transfer->mutex );
   177   	transfer->mutex = NULL;
   178   }
   179   
   180   if ( transfer->control_sock != NULL )  
   181   {
   182   	g_object_unref( transfer->control_sock );
   183   	transfer->control_sock = NULL;
   184   }  
   185   
   186   if ( transfer->sock != NULL )  
   187   {
   188   	g_object_unref( transfer->sock );
   189   	transfer->sock = NULL;
   190   }
   191     
   192   if ( transfer->filename != NULL )  
   193   {
   194   	g_free( transfer->filename );
   195   	transfer->filename = NULL;
   196   }
   197 
   198   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->dispose (object);
   199 }
   200 
   201 static void
   202 gmyth_file_transfer_finalize (GObject *object)
   203 {
   204   g_signal_handlers_destroy (object);
   205 
   206   G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->finalize (object);
   207 }
   208 
   209 // fixme: do we need the card_id????
   210 GMythFileTransfer*
   211 gmyth_file_transfer_new ( const GMythBackendInfo *backend_info)
   212 {
   213   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   214   
   215   transfer->backend_info = (GMythBackendInfo *)backend_info;
   216   g_object_ref (transfer->backend_info);
   217 
   218   return transfer;
   219 }
   220 
   221 GMythFileTransfer* 
   222 gmyth_file_transfer_new_with_uri (const gchar* uri_str)
   223 {
   224   GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   225 
   226   transfer->backend_info = gmyth_backend_info_new_with_uri (uri_str);
   227 
   228   return transfer;
   229 }
   230 
   231 gboolean
   232 gmyth_file_transfer_open (GMythFileTransfer *transfer, const gchar* filename)
   233 {
   234   gboolean ret = TRUE;
   235   
   236   g_return_val_if_fail (transfer != NULL, FALSE);
   237   g_return_val_if_fail (filename != NULL, FALSE);
   238   
   239   if (transfer->filename != NULL)
   240   {
   241     g_free (transfer->filename);
   242     transfer->filename = NULL;
   243   }
   244 
   245   transfer->filename = g_strdup (filename);
   246   
   247   /* configure the control socket */
   248   if (transfer->control_sock == NULL) { 
   249     if (!gmyth_connect_to_backend (transfer)) {
   250       gmyth_debug ("Connection to backend failed (Control Socket).\n");
   251       ret = FALSE;
   252     }
   253   } else {
   254     g_warning("Remote transfer control socket already created.\n");
   255   }
   256   
   257   gmyth_debug ("Got file with size = %lld.\n", transfer->filesize);
   258   
   259   return ret;
   260 
   261 }
   262 
   263 static gboolean
   264 gmyth_connect_to_backend (GMythFileTransfer *transfer)
   265 {
   266   GString *base_str = NULL;
   267   GString *hostname = NULL;
   268   GMythStringList *strlist = NULL; 
   269   gboolean ret = TRUE;
   270 
   271   g_return_val_if_fail (transfer != NULL, FALSE );
   272 
   273   base_str = g_string_new ("");
   274 
   275   /* Creates the control socket */
   276   if (transfer->control_sock != NULL) {
   277     g_object_unref (transfer->control_sock);
   278     transfer->control_sock = NULL;
   279   }
   280 
   281   transfer->control_sock = gmyth_socket_new();
   282 
   283   // Connects the socket, send Mythtv ANN command and verify Mythtv protocol version 
   284   if (!gmyth_socket_connect_to_backend (transfer->control_sock,
   285         transfer->backend_info->hostname, transfer->backend_info->port, TRUE)) {
   286 
   287     g_object_unref (transfer->control_sock);
   288     transfer->control_sock = NULL;
   289     return FALSE;
   290   }
   291     
   292   /* Creates the data socket */
   293   if (transfer->sock != NULL) {
   294     g_object_unref (transfer->sock);
   295     transfer->sock = NULL;
   296   }
   297 
   298   transfer->sock = gmyth_socket_new ();
   299   gmyth_socket_connect (transfer->sock, transfer->backend_info->hostname, transfer->backend_info->port);
   300 
   301   strlist = gmyth_string_list_new();
   302   hostname = gmyth_socket_get_local_hostname();
   303   gmyth_debug( "[%s] MythTV version (from backend) = %d.\n", __FUNCTION__, transfer->control_sock->mythtv_version );
   304   if ( transfer->control_sock->mythtv_version > 26 )
   305   	g_string_printf( base_str, "ANN FileTransfer %s 1 -1", hostname->str);
   306   else
   307   	g_string_printf( base_str, "ANN FileTransfer %s", hostname->str);
   308 
   309   gmyth_string_list_append_string (strlist, base_str );
   310   gmyth_string_list_append_char_array (strlist, transfer->filename);
   311 
   312   gmyth_socket_write_stringlist (transfer->sock, strlist );
   313   gmyth_socket_read_stringlist (transfer->sock, strlist );
   314     
   315   /* file identification used in future file transfer requests to backend */
   316   transfer->file_id = gmyth_string_list_get_int( strlist, 1 );
   317 
   318   /* Myth URI stream file size - decoded using two 8-bytes sequences (64 bits/long long types) */
   319   transfer->filesize = gmyth_util_decode_long_long( strlist, 2 );
   320 
   321   gmyth_debug ( "[%s] ***** Received: recordernum = %d, filesize = %" G_GUINT64_FORMAT "\n", __FUNCTION__,
   322           transfer->file_id, transfer->filesize );
   323 
   324   if (transfer->filesize < 0 ) {
   325       gmyth_debug ( "[%s] Got filesize equals to %llu is lesser than 0 [invalid stream file]\n", __FUNCTION__, transfer->filesize );
   326       g_object_unref (transfer->sock);
   327       transfer->sock = NULL;
   328       ret = FALSE;
   329       goto cleanup;
   330   }
   331   
   332 cleanup:
   333 
   334   if ( strlist != NULL )
   335     g_object_unref( strlist );
   336 
   337   g_string_free (base_str, TRUE);
   338   g_string_free (hostname, TRUE);
   339 
   340   return ret;
   341 }    
   342 
   343 gboolean
   344 gmyth_file_transfer_is_open (GMythFileTransfer *transfer)
   345 {
   346     GMythStringList *strlist;
   347     GString *query;
   348 
   349     g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   350     g_return_val_if_fail (transfer->sock != NULL, FALSE);
   351 
   352     strlist = gmyth_string_list_new();
   353     query = g_string_new (GMYTHTV_QUERY_HEADER);
   354     g_string_append_printf (query, "%d", transfer->file_id );
   355 
   356     gmyth_string_list_append_string (strlist, query );
   357     gmyth_string_list_append_char_array( strlist, "IS_OPEN" );
   358 
   359     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   360     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   361 
   362     g_string_free (query, TRUE);
   363     g_object_unref (strlist);
   364 
   365     return ( strlist != NULL && gmyth_string_list_get_int( strlist, 0 ) == 1 );
   366 }
   367 
   368 void
   369 gmyth_file_transfer_close( GMythFileTransfer *transfer )
   370 {
   371   GMythStringList *strlist;
   372   GString *query;
   373 
   374   g_return_if_fail (transfer->control_sock != NULL);
   375 
   376   strlist = gmyth_string_list_new( );
   377   query = g_string_new (GMYTHTV_QUERY_HEADER);
   378   g_string_append_printf( query, "%d", transfer->file_id);
   379 
   380   gmyth_string_list_append_string( strlist, query );
   381   gmyth_string_list_append_char_array( strlist, "DONE" );
   382 
   383   if ( gmyth_socket_sendreceive_stringlist(transfer->control_sock, strlist) <= 0 ) {
   384     // fixme: time out???
   385     g_printerr( "Remote file timeout.\n" );
   386   }
   387 
   388   g_string_free (query, TRUE);
   389   g_object_unref (strlist);
   390 
   391   if (transfer->sock) {
   392     g_object_unref( transfer->sock );
   393     transfer->sock = NULL;
   394   }
   395 
   396   if (transfer->control_sock) {
   397     g_object_unref( transfer->control_sock );
   398     transfer->control_sock = NULL;
   399   } 
   400   
   401 }
   402 
   403 gint64
   404 gmyth_file_transfer_seek(GMythFileTransfer *transfer, guint64 pos, gint whence)
   405 {
   406   GMythStringList *strlist = gmyth_string_list_new();
   407   GString *query;
   408 
   409   g_return_val_if_fail (transfer->sock != NULL, -1);
   410   g_return_val_if_fail (transfer->control_sock != NULL, -1);
   411 
   412   strlist = gmyth_string_list_new();
   413   query = g_string_new (GMYTHTV_QUERY_HEADER);
   414   g_string_append_printf (query, "%d", transfer->file_id);
   415   
   416   myth_control_acquire_context( TRUE );
   417 
   418   gmyth_string_list_append_string( strlist, query );
   419   gmyth_string_list_append_char_array( strlist, "SEEK" );
   420   gmyth_string_list_append_uint64( strlist, pos );
   421   
   422   gmyth_string_list_append_int( strlist, whence );  
   423 
   424   if (pos > 0 )
   425     gmyth_string_list_append_uint64( strlist, pos );
   426   else
   427     gmyth_string_list_append_uint64( strlist, transfer->readposition );
   428   
   429   gmyth_socket_sendreceive_stringlist( transfer->control_sock, strlist );
   430 
   431   gint64 retval = gmyth_string_list_get_int64(strlist, 0);
   432   transfer->readposition = retval;
   433   gmyth_debug ( "[%s] got reading position pointer from the streaming = %lld\n", 
   434       __FUNCTION__, retval );
   435 
   436   myth_control_release_context( );
   437 
   438   return retval;
   439 }
   440 
   441 static gboolean 
   442 myth_control_acquire_context( gboolean do_wait )
   443 {
   444 	gboolean ret = TRUE;	
   445 	//guint max_iter = 50;
   446 	
   447 	//g_mutex_lock( mutex );
   448 	
   449   //while ( !has_io_access ) 
   450   //	g_cond_wait( io_watcher_cond, mutex );
   451   	
   452   //has_io_access = FALSE;
   453   
   454   //myth_control_acquire_context (FALSE);
   455    
   456   /* 
   457   if ( do_wait ) {
   458   	while ( --max_iter > 0 && !g_main_context_wait( io_watcher_context, io_watcher_cond, mutex ) )
   459   		ret = FALSE;
   460   } else if ( !g_main_context_acquire( io_watcher_context ) )
   461   	ret = FALSE;
   462   */
   463   	
   464   //g_static_mutex_lock( &st_mutex );
   465   
   466   return ret;
   467 }
   468 
   469 static gboolean 
   470 myth_control_release_context( ) 
   471 {
   472 	gboolean ret = TRUE;
   473     
   474   //g_static_mutex_unlock( &st_mutex );
   475   
   476 	//g_main_context_release( io_watcher_context );
   477   
   478   //g_main_context_wakeup( io_watcher_context );
   479   
   480   //has_io_access = TRUE;
   481 
   482   //g_cond_broadcast( io_watcher_cond );
   483   
   484   //g_mutex_unlock( mutex );  
   485  
   486   return ret;
   487 }
   488 
   489 gint 
   490 gmyth_file_transfer_read(GMythFileTransfer *transfer, GByteArray *data, gint size, gboolean read_unlimited)
   491 {
   492   gint bytes_sent = 0;
   493   gsize bytes_read = 0;
   494   gsize total_read = 0;
   495   
   496   GError *error = NULL;
   497   
   498   GIOChannel *io_channel;
   499   GIOChannel *io_channel_control;
   500 
   501   GIOCondition io_cond;
   502   GIOCondition io_cond_control;
   503   GIOStatus io_status = G_IO_STATUS_NORMAL, io_status_control = G_IO_STATUS_NORMAL;
   504   
   505   gboolean ret = TRUE;   
   506 
   507   GString *query;
   508   
   509   g_return_val_if_fail ( data != NULL, -2 );
   510 
   511   io_channel = transfer->sock->sd_io_ch;
   512   io_channel_control = transfer->control_sock->sd_io_ch;
   513 
   514   io_status = g_io_channel_set_encoding( io_channel, NULL, &error );
   515   if ( io_status == G_IO_STATUS_NORMAL )
   516     gmyth_debug ( "[%s] Setting encoding to binary data socket).\n", __FUNCTION__ );
   517 
   518   io_cond = g_io_channel_get_buffer_condition( io_channel );  
   519 
   520   io_cond_control = g_io_channel_get_buffer_condition( io_channel );
   521   if ( transfer->sock == NULL || ( io_status == G_IO_STATUS_ERROR ) ) {
   522     g_printerr( "gmyth_file_transfer_read(): Called with no raw socket.\n" );
   523     //exit(0); // fixme remove this
   524     return -1;
   525   }
   526 
   527   if ( transfer->control_sock == NULL || ( io_status_control == G_IO_STATUS_ERROR ) ) {
   528     g_printerr( "gmyth_file_transfer_read(): Called with no control socket.\n" );
   529     //exit(0); // fixme remove this
   530     return -1;
   531   }
   532 
   533   query = g_string_new (GMYTHTV_QUERY_HEADER);
   534   g_string_append_printf ( query, "%d", transfer->file_id );
   535   gmyth_debug ("[%s] Transfer_query = %s\n", __FUNCTION__, query->str );
   536   
   537   /* send requests to the maximum number of REQUEST_BLOCK messages */
   538   guint max_tries = 5;
   539   
   540   myth_control_acquire_context( TRUE );
   541   
   542   while (total_read == 0 && --max_tries > 0) {
   543     GMythStringList *strlist = gmyth_string_list_new();
   544 
   545     gmyth_string_list_append_char_array( strlist, query->str );
   546     gmyth_string_list_append_char_array( strlist, "REQUEST_BLOCK" );
   547     gmyth_string_list_append_int( strlist, size );
   548 
   549     // Request the block to the backend
   550     gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   551 
   552     // Receives the backand answer    
   553     gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   554 
   555     if ( strlist != NULL && gmyth_string_list_length( strlist ) > 0 ) 
   556     { 
   557 	    bytes_sent = gmyth_string_list_get_int( strlist,  0 ); // -1 on backend error
   558 	    gmyth_debug ( "[%s] got SENT buffer message = %d\n", __FUNCTION__, bytes_sent );
   559 
   560     	if ( bytes_sent != 0 ) 
   561     	{
   562         gchar *data_buffer = g_new0 ( gchar, bytes_sent );    
   563    	    while ( 0 != bytes_sent ) 
   564    	    { 
   565           io_status = g_io_channel_read_chars( io_channel, data_buffer + total_read, 
   566 	    				(gsize) bytes_sent,	&bytes_read, &error );
   567 
   568 	        total_read += bytes_read;
   569           bytes_sent -= bytes_read;
   570       
   571             /* append new data to the increasing byte array */
   572 	    		data = g_byte_array_append (data, (const guint8*)data_buffer, bytes_read);
   573 	        gmyth_debug ("Total transfer data read: %d\n", total_read);
   574         }
   575         g_free (data_buffer);
   576 	    } else {
   577 				transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);
   578 				
   579 				if ( transfer->priv != NULL && transfer->priv->livetv != NULL )
   580 				{
   581 				
   582 					total_read = GMYTHTV_FILE_TRANSFER_NEXT_PROG_CHAIN;	
   583 				
   584 			  	if ( transfer->priv->do_next_program_chain )
   585 			  	{			  					  		
   586 			  		
   587 			  		g_mutex_lock( transfer->mutex );
   588 			  		
   589 			  		ret = gmyth_livetv_next_program_chain( transfer->priv->livetv );
   590 			  		
   591 			  		g_mutex_unlock( transfer->mutex );
   592 			  		
   593 						if ( !ret )
   594 						{
   595 							gmyth_debug( "Cannot change to the next program chain!" );
   596 						}
   597 						else
   598 						{
   599 							gmyth_debug( "OK!!! MOVED to the next program chain [%s]!", 
   600 													(gmyth_tvchain_get_id( transfer->priv->livetv->tvchain ))->str );
   601 						}
   602 						
   603 			  	}
   604 			  	
   605 				} /* if */
   606       } /* if */
   607     } else {
   608     	total_read = GMYTHTV_FILE_TRANSFER_READ_ERROR;
   609     	g_object_unref (strlist);
   610     	strlist = NULL; 
   611     	break;
   612     }
   613     g_object_unref (strlist);
   614     strlist = NULL;
   615   }
   616   
   617   myth_control_release_context( );
   618   g_string_free (query, TRUE);
   619 
   620   if ( error != NULL ) {
   621     gmyth_debug ("Cleaning-up ERROR: %s [msg = %s, code = %d]\n", __FUNCTION__, error->message, 
   622 	    error->code);
   623     g_error_free (error);
   624   }
   625   
   626   if ( total_read > 0 )
   627   	transfer->readposition += total_read;  	
   628   	
   629   return total_read;
   630 }
   631 
   632 static void 
   633 gmyth_file_transfer_program_info_changed( GMythFileTransfer *transfer, 
   634 										gint msg_code, gpointer livetv_transfer, gpointer user_data )
   635 {
   636 	GMythLiveTV *livetv = (GMythLiveTV*)user_data;
   637 	
   638 	gmyth_debug( "Program info changed! ( file transfer orig. = %p, ptr. = [%s], user data = [%s] )", transfer, 
   639 		livetv_transfer != NULL ? "[NOT NULL]" : "[NULL]", livetv != NULL ? "[NOT NULL]" : "[NULL]" );
   640 	
   641 	if ( livetv != NULL && transfer == livetv->file_transfer )
   642 	{
   643 		gmyth_debug( "YES, the requested program info movement on the LiveTV transfer is authentical!" );
   644 	}
   645 	
   646 	transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE(transfer);	
   647 	
   648 	transfer->priv->livetv = livetv;	
   649 	transfer->priv->do_next_program_chain = TRUE;
   650 	
   651 	//g_object_unref( transfer );
   652 }
   653 
   654 gboolean 
   655 gmyth_file_transfer_settimeout( GMythFileTransfer *transfer, gboolean fast )
   656 {
   657 
   658   GMythStringList *strlist = NULL;
   659 
   660   g_return_val_if_fail (transfer->sock != NULL, FALSE);
   661   g_return_val_if_fail (transfer->control_sock != NULL, FALSE);
   662 
   663 //  if ( transfer->timeoutisfast == fast )
   664 //    return;
   665 
   666   strlist = gmyth_string_list_new(); 
   667   gmyth_string_list_append_char_array( strlist, GMYTHTV_QUERY_HEADER );
   668   gmyth_string_list_append_char_array( strlist, "SET_TIMEOUT" );
   669   gmyth_string_list_append_int( strlist, fast ); 
   670 
   671   gmyth_socket_write_stringlist( transfer->control_sock, strlist );
   672   gmyth_socket_read_stringlist( transfer->control_sock, strlist );
   673 
   674 //  transfer->timeoutisfast = fast;
   675 
   676   return TRUE;
   677 }
   678 
   679 
   680 guint64
   681 gmyth_file_transfer_get_filesize (GMythFileTransfer *transfer)
   682 {
   683     g_return_val_if_fail (transfer != NULL, 0);
   684     return transfer->filesize;
   685 }