gmyth/src/gmyth_file_transfer.c
author rosfran
Fri May 18 01:34:31 2007 +0100 (2007-05-18)
branchtrunk
changeset 689 74c1f4270cf0
parent 685 e863d5b45e52
child 690 ba11bfc5f239
permissions -rwxr-xr-x
[svn r695] Fixed erroneous maximum iteration count on reading zero-sized buffers on FileTransfer.
     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 
    61 #define GMYTH_FILE_TRANSFER_GET_PRIVATE(obj) \
    62     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GMYTH_FILE_TRANSFER_TYPE, GMythFileTransferPrivate))
    63 
    64 enum myth_sock_types
    65     {
    66         GMYTH_PLAYBACK_TYPE = 0,
    67         GMYTH_MONITOR_TYPE,
    68         GMYTH_FILETRANSFER_TYPE,
    69         GMYTH_RINGBUFFER_TYPE
    70     };
    71 
    72 struct _GMythFileTransferPrivate
    73     {
    74         GMythRecorder *recorder;
    75 
    76         gboolean do_next_program_chain;
    77         gboolean disposed;
    78         gboolean livetv_wait;
    79 
    80         /* MythTV version number */
    81         gint mythtv_version;
    82 
    83         /* socket descriptors */
    84         GMythSocket *control_sock;
    85         GMythSocket *sock;
    86         GMutex *mutex;
    87         gint file_id;
    88     };
    89 
    90 static void gmyth_file_transfer_class_init (GMythFileTransferClass * klass);
    91 static void gmyth_file_transfer_init (GMythFileTransfer * object);
    92 static void gmyth_file_transfer_dispose (GObject * object);
    93 static void gmyth_file_transfer_finalize (GObject * object);
    94 static void _file_transfer_program_info_changed (GMythFileTransfer * transfer,
    95                                                  gint msg_code,
    96                                                  gpointer livetv_recorder);
    97 static gboolean _connect_to_backend (GMythFileTransfer * transfer);
    98 static gboolean _control_acquire_context (GMythFileTransfer * transfer,
    99                                           gboolean do_wait);
   100 static gboolean _control_release_context (GMythFileTransfer * transfer);
   101 
   102 G_DEFINE_TYPE (GMythFileTransfer, gmyth_file_transfer, GMYTH_FILE_TYPE)
   103      static void gmyth_file_transfer_class_init (GMythFileTransferClass * klass)
   104 {
   105     GObjectClass *gobject_class;
   106     GMythFileTransferClass *gtransfer_class;
   107 
   108     gobject_class = (GObjectClass *) klass;
   109     gtransfer_class = (GMythFileTransferClass *) gobject_class;
   110 
   111     gobject_class->dispose = gmyth_file_transfer_dispose;
   112     gobject_class->finalize = gmyth_file_transfer_finalize;
   113 
   114     g_type_class_add_private (gobject_class, sizeof (GMythFileTransferPrivate));
   115 
   116     gtransfer_class->program_info_changed_handler =
   117         _file_transfer_program_info_changed;
   118 
   119     gtransfer_class->program_info_changed_handler_signal_id =
   120         g_signal_new ("program-info-changed",
   121                       G_TYPE_FROM_CLASS (gtransfer_class),
   122                       G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE |
   123                       G_SIGNAL_NO_HOOKS, 0, NULL, NULL,
   124                       gmyth_marshal_VOID__INT_POINTER, G_TYPE_NONE, 2,
   125                       G_TYPE_INT, G_TYPE_POINTER);
   126 
   127 }
   128 
   129 static void
   130 gmyth_file_transfer_init (GMythFileTransfer * transfer)
   131 {
   132     g_return_if_fail (transfer != NULL);
   133 
   134     transfer->priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   135     transfer->priv->mutex = g_mutex_new ();
   136 
   137     g_signal_connect (G_OBJECT (transfer), "program-info-changed",
   138                       (GCallback) (GMYTH_FILE_TRANSFER_GET_CLASS (transfer)->
   139                                    program_info_changed_handler), NULL);
   140 }
   141 
   142 static void
   143 gmyth_file_transfer_dispose (GObject * object)
   144 {
   145     GMythFileTransferPrivate *priv;
   146     GMythFileTransfer *transfer = GMYTH_FILE_TRANSFER (object);
   147 
   148     g_return_if_fail (transfer != NULL);
   149 
   150     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   151 
   152     if (priv->disposed)
   153     {
   154         /* If dispose did already run, return. */
   155         return;
   156     }
   157 
   158     /* Make sure dispose does not run twice. */
   159     priv->disposed = TRUE;
   160 
   161     if (priv->mutex != NULL)
   162     {
   163         g_mutex_free (priv->mutex);
   164         priv->mutex = NULL;
   165     }
   166 
   167     if (priv->control_sock != NULL)
   168     {
   169         g_object_unref (priv->control_sock);
   170         priv->control_sock = NULL;
   171     }
   172 
   173     if (priv->sock != NULL)
   174     {
   175         g_object_unref (priv->sock);
   176         priv->sock = NULL;
   177     }
   178 
   179     if (priv->recorder != NULL)
   180     {
   181         g_object_unref (priv->recorder);
   182         priv->recorder = NULL;
   183     }
   184 
   185     G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->dispose (object);
   186 }
   187 
   188 static void
   189 gmyth_file_transfer_finalize (GObject * object)
   190 {
   191     g_signal_handlers_destroy (object);
   192 
   193     G_OBJECT_CLASS (gmyth_file_transfer_parent_class)->finalize (object);
   194 }
   195 
   196 /** 
   197  * Creates a new instance of GMythFileTransfer.
   198  * 
   199  * @param backend_info The BackendInfo instance, with all the MythTV network 
   200  * 										 configuration data.
   201  * 
   202  * @return a new instance of the File Transfer. 
   203  */
   204 GMythFileTransfer *
   205 gmyth_file_transfer_new (GMythBackendInfo * backend_info)
   206 {
   207     GMythFileTransfer *transfer = g_object_new (GMYTH_FILE_TRANSFER_TYPE,
   208                                                 "backend-info", backend_info,
   209                                                 NULL);
   210     //GValue val = {0,};    
   211     //backend_info = g_object_ref( backend_info );
   212     gmyth_debug ("Creating FileTransfer BackendInfo hostname = %s",
   213                  gmyth_backend_info_get_hostname (backend_info));
   214     //GMythBackendInfo *backend_info = gmyth_backend_info_new_with_uri (uri_str);
   215     //g_value_init (&val, G_TYPE_OBJECT);
   216     //g_value_set_object (&val, backend_info);    
   217     //g_object_set (G_OBJECT (transfer), "backend-info", &val, NULL);
   218 
   219     return transfer;
   220 }
   221 
   222 gchar *
   223 gmyth_file_transfer_get_file_name (GMythFileTransfer * transfer)
   224 {
   225     gchar *filename;
   226 
   227     g_object_get (G_OBJECT (transfer), "filename", &filename, NULL);
   228 
   229     return filename;
   230 }
   231 
   232 /** 
   233  * Creates a new instance of GMythFileTransfer.
   234  * 
   235  * @param uri_str The URI poiting to the MythTV backend server.
   236  * 
   237  * @return a new instance of the File Transfer. 
   238  */
   239 GMythFileTransfer *
   240 gmyth_file_transfer_new_with_uri (const gchar * uri_str)
   241 {
   242     GMythFileTransfer *transfer =
   243         GMYTH_FILE_TRANSFER (g_object_new (GMYTH_FILE_TRANSFER_TYPE, NULL));
   244     gmyth_debug ("URI str = %s", uri_str);
   245     //GMythBackendInfo *backend_info = gmyth_backend_info_new_with_uri (uri_str);
   246     GValue val = { 0, };
   247     g_value_init (&val, G_TYPE_OBJECT);
   248     g_value_set_object (&val, gmyth_backend_info_new_with_uri (uri_str));
   249     g_object_set (G_OBJECT (transfer), "backend-info", &val, NULL);
   250 
   251     return transfer;
   252 }
   253 
   254 /** 
   255  * Open a File Transfer connection in order to get a remote file.
   256  * 
   257  * @param transfer The actual File Transfer instance. 
   258  * @param filename The file name of the remote file to be transferred to the client.
   259  * 
   260  * @return <code>true</code>, if the connection opening had been done successfully. 
   261  */
   262 gboolean
   263 gmyth_file_transfer_open (GMythFileTransfer * transfer, const gchar * filename)
   264 {
   265     gboolean ret = TRUE;
   266     GMythFileTransferPrivate *priv;
   267 
   268     g_return_val_if_fail (transfer != NULL, FALSE);
   269     g_return_val_if_fail (filename != NULL && strlen (filename) > 0, FALSE);
   270 
   271     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   272     
   273     gmyth_debug ("Opening the FileTransfer... (%s)", filename);
   274 
   275     g_object_set (GMYTH_FILE (transfer), "filename", filename, NULL);
   276 
   277     /* configure the control socket */
   278     if (TRUE/*priv->control_sock == NULL*/)
   279     {
   280         if (!_connect_to_backend (transfer))
   281         {
   282             gmyth_debug ("Connection to backend failed (Control Socket).");
   283             ret = FALSE;
   284         }
   285 
   286 	if (priv->do_next_program_chain) {
   287             priv->do_next_program_chain = FALSE; // fixme
   288 	    gmyth_debug ("New file available before the current file was opened");
   289             GMythProgramInfo *prog_info =
   290                 gmyth_recorder_get_current_program_info (priv->recorder);
   291 
   292             if (prog_info != NULL && prog_info->pathname != NULL
   293                 && strlen (prog_info->pathname->str) > 0
   294                 && g_ascii_strcasecmp (prog_info->pathname->str,
   295                                        gmyth_file_get_file_name (GMYTH_FILE
   296                                                                  (transfer))) !=
   297                 0)
   298                 ret =
   299                     gmyth_file_transfer_open (transfer,
   300                                               g_strrstr (prog_info->pathname->
   301                                                          str, "/"));
   302 
   303             if (prog_info != NULL)
   304                 g_object_unref (prog_info);
   305 
   306             if (!ret)
   307                 gmyth_debug ("Cannot change to the next program info!");
   308             else
   309                 gmyth_debug ("OK!!! MOVED to the next program info [%s]!",
   310                              gmyth_file_get_file_name (GMYTH_FILE (transfer)));
   311 
   312 	} else {
   313 	    gmyth_debug ("XXXXXXXXXXXXXX Oulu tests XXXXXXXXXXXXXXXX");
   314 	    gmyth_debug ("None new file found. We continue with the same file opened before");
   315 	}
   316 
   317     }
   318     else
   319     {
   320         gmyth_debug ("Remote transfer control socket already created.");
   321     }
   322 
   323     gmyth_debug ("Got file with size = %lld.\n",
   324                  gmyth_file_get_filesize (GMYTH_FILE (transfer)));
   325 
   326     return ret;
   327 }
   328 
   329 /** 
   330  * Connect a File Transfer binary client socket to a remote file.
   331  * 
   332  * @param transfer The actual File Transfer instance. 
   333  * 
   334  * @return <code>true</code>, if the connection had been configured successfully. 
   335  */
   336 static gboolean
   337 _connect_to_backend (GMythFileTransfer * transfer)
   338 {
   339     GString *base_str = NULL;
   340     GString *hostname = NULL;
   341     GMythStringList *strlist = NULL;
   342     gboolean ret = TRUE;
   343     GMythFileTransferPrivate *priv;
   344     GMythBackendInfo *backend_info;
   345 
   346     g_return_val_if_fail (transfer != NULL, FALSE);
   347 
   348     g_object_get (GMYTH_FILE (transfer), "backend-info", &backend_info, NULL);
   349 
   350     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   351     _control_acquire_context (transfer, TRUE);
   352 
   353     /* Creates the control socket */
   354 
   355     if (priv->control_sock != NULL)
   356     {
   357         g_object_unref (priv->control_sock);
   358         priv->control_sock = NULL;
   359     }
   360 
   361     base_str = g_string_new ("");
   362 
   363     priv->control_sock = gmyth_socket_new ();
   364     // Connects the socket, send Mythtv ANN command and verify Mythtv protocol version 
   365     if (!gmyth_socket_connect_to_backend (priv->control_sock,
   366                                           backend_info->hostname,
   367                                           backend_info->port, TRUE))
   368     {
   369 
   370         g_object_unref (priv->control_sock);
   371         priv->control_sock = NULL;
   372         return FALSE;
   373     }
   374 
   375     /* Creates the data socket */
   376     if (priv->sock != NULL)
   377     {
   378         g_object_unref (priv->sock);
   379         priv->sock = NULL;
   380     }
   381 
   382     priv->sock = gmyth_socket_new ();
   383     gmyth_socket_connect (priv->sock, backend_info->hostname,
   384                           backend_info->port);
   385     gmyth_debug ("Connecting file transfer... (%s, %d)", backend_info->hostname,
   386                  backend_info->port);
   387 
   388     strlist = gmyth_string_list_new ();
   389     hostname = gmyth_socket_get_local_hostname ();
   390     gmyth_debug ("[%s] MythTV version (from backend) = %d.\n", __FUNCTION__,
   391                  priv->control_sock->mythtv_version);
   392     if (priv->control_sock->mythtv_version > 26)
   393         g_string_printf (base_str, "ANN FileTransfer %s 1 -1", hostname->str);
   394     else
   395         g_string_printf (base_str, "ANN FileTransfer %s", hostname->str);
   396 
   397     gmyth_string_list_append_string (strlist, base_str);
   398     gmyth_string_list_append_char_array (strlist,
   399                                          gmyth_file_get_file_name (GMYTH_FILE
   400                                                                    (transfer)));
   401 
   402     gmyth_socket_write_stringlist (priv->sock, strlist);
   403 
   404     /* MONITOR Handler - DVB TV Chain update messages!!! */
   405 
   406     gmyth_socket_read_stringlist (priv->sock, strlist);
   407 
   408     /* file identification used in future file transfer requests to backend */
   409     priv->file_id = gmyth_string_list_get_int (strlist, 1);
   410 
   411     /* Myth URI stream file size - decoded using two 8-bytes sequences (64 bits/long long types) */
   412     gmyth_file_set_filesize (GMYTH_FILE (transfer),
   413                              gmyth_string_list_get_int64 (strlist, 2));
   414 
   415     gmyth_debug ("***** Received: recordernum = %d, filesize = %"
   416                  G_GUINT64_FORMAT "\n", priv->file_id,
   417                  gmyth_file_get_filesize (GMYTH_FILE (transfer)));
   418 
   419     if (gmyth_file_get_filesize (GMYTH_FILE (transfer)) < 0)
   420     {
   421         gmyth_debug
   422             ("Got filesize equals to %llu is lesser than 0 [invalid stream file]\n",
   423              gmyth_file_get_filesize (GMYTH_FILE (transfer)));
   424         g_object_unref (priv->sock);
   425         priv->sock = NULL;
   426         ret = FALSE;
   427     }
   428 
   429     _control_release_context (transfer);
   430 
   431     if (strlist != NULL)
   432         g_object_unref (strlist);
   433 
   434     if (base_str != NULL)
   435         g_string_free (base_str, TRUE);
   436 
   437     if (hostname != NULL)
   438         g_string_free (hostname, TRUE);
   439 
   440     return ret;
   441 }
   442 
   443 /** 
   444  * Receives a GObject signal coming from a LiveTV instance, all the time a 
   445  * program info changes.
   446  * 
   447  * @param transfer The actual File Transfer instance. 
   448  * @param msg_code The MythTV backend message status code.
   449  * @param live_tv A pointer to the LiveTV instance. * 
   450  */
   451 void
   452 gmyth_file_transfer_emit_program_info_changed_signal (GMythFileTransfer *
   453                                                       transfer, gint msg_code,
   454                                                       gpointer live_tv_recorder)
   455 {
   456     gmyth_debug ("Calling signal handler... [FILE_TRANSFER]");
   457 
   458     g_signal_emit (transfer, GMYTH_FILE_TRANSFER_GET_CLASS (transfer)->program_info_changed_handler_signal_id, 0,       /* details */
   459                    msg_code, live_tv_recorder);
   460 
   461 }
   462 
   463 /** 
   464  * Checks if the actual File Transfer connection is open.
   465  * 
   466  * @param transfer The actual File Transfer instance. 
   467  * 
   468  * @return <code>true</code>, if the File Transfer connection is opened. 
   469  */
   470 gboolean
   471 gmyth_file_transfer_is_open (GMythFileTransfer * transfer)
   472 {
   473     GMythStringList *strlist;
   474     GMythFileTransferPrivate *priv;
   475     GString *query;
   476 
   477     g_return_val_if_fail (transfer != NULL, FALSE);
   478 
   479     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   480     g_return_val_if_fail (priv->control_sock != NULL, FALSE);
   481     g_return_val_if_fail (priv->sock != NULL, FALSE);
   482 
   483     _control_acquire_context (transfer, TRUE);
   484 
   485     strlist = gmyth_string_list_new ();
   486     query = g_string_new (GMYTHTV_QUERY_HEADER);
   487     g_string_append_printf (query, "%d", priv->file_id);
   488 
   489     gmyth_string_list_append_string (strlist, query);
   490     gmyth_string_list_append_char_array (strlist, "IS_OPEN");
   491 
   492     gmyth_socket_write_stringlist (priv->control_sock, strlist);
   493     gmyth_socket_read_stringlist (priv->control_sock, strlist);
   494 
   495     _control_release_context (transfer);
   496 
   497     g_string_free (query, TRUE);
   498     g_object_unref (strlist);
   499 
   500     return (strlist != NULL && gmyth_string_list_get_int (strlist, 0) == 1);
   501 }
   502 
   503 /** 
   504  * Closes a remote File Transfer connection.
   505  * 
   506  * @param transfer The actual File Transfer instance. 
   507  */
   508 void
   509 gmyth_file_transfer_close (GMythFileTransfer * transfer)
   510 {
   511     GMythStringList *strlist;
   512     GMythFileTransferPrivate *priv;
   513     GString *query;
   514 
   515     g_return_if_fail (transfer != NULL);
   516 
   517     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   518 
   519     if (priv->control_sock == NULL)
   520         return;
   521 
   522     _control_acquire_context (transfer, TRUE);
   523 
   524     strlist = gmyth_string_list_new ();
   525     query = g_string_new (GMYTHTV_QUERY_HEADER);
   526     g_string_append_printf (query, "%d", priv->file_id);
   527 
   528     gmyth_string_list_append_string (strlist, query);
   529     gmyth_string_list_append_char_array (strlist, "DONE");
   530 
   531     if (gmyth_socket_sendreceive_stringlist (priv->control_sock, strlist) <= 0)
   532     {
   533         // fixme: time out???
   534         gmyth_debug ("Remote file timeout.\n");
   535     }
   536 
   537     g_string_free (query, TRUE);
   538     g_object_unref (strlist);
   539 
   540     if (priv->sock)
   541     {
   542         g_object_unref (priv->sock);
   543         priv->sock = NULL;
   544     }
   545 
   546     if (priv->control_sock)
   547     {
   548         g_object_unref (priv->control_sock);
   549         priv->control_sock = NULL;
   550     }
   551 
   552     _control_release_context (transfer);
   553 }
   554 
   555 /** 
   556  * Do a seek operation (moves the read/write file pointer) on a remote file.
   557  * 
   558  * @param transfer The actual File Transfer instance. 
   559  * @param pos The position to be moved in the remote file.
   560  * @param whence Tells to what direction seek movement should be done.
   561  * 
   562  * @return The actual position on the remote file (after seek has been done). 
   563  */
   564 gint64
   565 gmyth_file_transfer_seek (GMythFileTransfer * transfer, guint64 pos,
   566                           gint whence)
   567 {
   568     GMythStringList *strlist = gmyth_string_list_new ();
   569     GMythFileTransferPrivate *priv;
   570     GString *query;
   571 
   572     g_return_val_if_fail (transfer != NULL, FALSE);
   573     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   574 
   575     g_return_val_if_fail (priv->sock != NULL, -1);
   576     g_return_val_if_fail (priv->control_sock != NULL, -1);
   577 
   578     strlist = gmyth_string_list_new ();
   579     query = g_string_new (GMYTHTV_QUERY_HEADER);
   580     g_string_append_printf (query, "%d", priv->file_id);
   581 
   582     /* myth_control_acquire_context( transfer, TRUE ); */
   583 
   584     gmyth_string_list_append_string (strlist, query);
   585     gmyth_string_list_append_char_array (strlist, "SEEK");
   586     gmyth_string_list_append_uint64 (strlist, pos);
   587 
   588     gmyth_string_list_append_int (strlist, whence);
   589 
   590     if (pos > 0)
   591         gmyth_string_list_append_uint64 (strlist, pos);
   592     else
   593         gmyth_string_list_append_uint64 (strlist,
   594                                          gmyth_file_get_offset (GMYTH_FILE
   595                                                                 (transfer)));
   596 
   597     gmyth_socket_sendreceive_stringlist (priv->control_sock, strlist);
   598 
   599     gint64 retval = gmyth_string_list_get_int64 (strlist, 0);
   600     gmyth_file_set_offset (GMYTH_FILE (transfer), retval);
   601     gmyth_debug ("Got reading position pointer from the streaming = %lld\n",
   602                  retval);
   603 
   604     g_object_unref (strlist);
   605     g_string_free (query, TRUE);
   606 
   607     /* myth_control_release_context( transfer ); */
   608 
   609     return retval;
   610 }
   611 
   612 /** 
   613  * Acquire access to a remote file socket read/write pointer.
   614  * 
   615  * @param transfer The actual File Transfer instance.
   616  * @param do_wait Waits or not on a GCond, when trying to read from the remote socket.
   617  * 
   618  * @return <code>true</code>, if the acquire had been got. 
   619  */
   620 static gboolean
   621 _control_acquire_context (GMythFileTransfer * transfer, gboolean do_wait)
   622 {
   623     gboolean ret = TRUE;
   624     GMythFileTransferPrivate *priv;
   625 
   626     g_return_val_if_fail (transfer != NULL, FALSE);
   627     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   628 
   629     g_mutex_lock (priv->mutex);
   630     return ret;
   631 }
   632 
   633 /** 
   634  * Release access to a remote file socket read/write pointer.
   635  * 
   636  * @param transfer The actual File Transfer instance.
   637  * 
   638  * @return <code>true</code>, if the socket read/write permissions had been releaseds. 
   639  */
   640 static gboolean
   641 _control_release_context (GMythFileTransfer * transfer)
   642 {
   643     gboolean ret = TRUE;
   644     GMythFileTransferPrivate *priv;
   645 
   646     g_return_val_if_fail (transfer != NULL, FALSE);
   647     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   648 
   649     g_mutex_unlock (priv->mutex);
   650 
   651     return ret;
   652 }
   653 
   654 /** 
   655  * Reads a block from a remote file.
   656  * 
   657  * @param transfer The actual File Transfer instance.
   658  * @param data A GByteArray instance, where all the binary data representing 
   659  * 						 the remote file will be stored.
   660  * @param size The block size, in bytes, to be requested from a remote file.
   661  * @param read_unlimited Tells the backend to read indefinitely (LiveTV), or only 
   662  * 											 gets the actual size
   663  * 
   664  * @return The actual block size (in bytes) returned by REQUEST_BLOCK message,
   665  * 				or the error code. 
   666  */
   667 GMythFileReadResult
   668 gmyth_file_transfer_read (GMythFileTransfer * transfer, GByteArray * data,
   669                           gint size, gboolean read_unlimited)
   670 {
   671     gint bytes_sent = 0;
   672     gsize bytes_read = 0;
   673     gint64 total_read = 0;
   674     GMythFileReadResult retval = GMYTH_FILE_READ_OK;
   675     GMythFileTransferPrivate *priv;
   676 
   677     GError *error = NULL;
   678 
   679     GIOChannel *io_channel;
   680     GIOChannel *io_channel_control;
   681 
   682     GIOCondition io_cond;
   683     GIOCondition io_cond_control;
   684     GIOStatus io_status = G_IO_STATUS_NORMAL;
   685     GIOStatus io_status_control = G_IO_STATUS_NORMAL;
   686 
   687     GMythStringList *strlist;
   688     GMythStringList *ret_strlist = NULL;
   689     gboolean ret = TRUE;
   690     GString *query;
   691 
   692     g_return_val_if_fail (transfer != NULL, FALSE);
   693     g_return_val_if_fail (data != NULL, GMYTH_FILE_READ_ERROR);
   694 
   695     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   696 
   697     strlist = gmyth_string_list_new ();
   698 
   699     io_channel = priv->sock->sd_io_ch;
   700     io_channel_control = priv->control_sock->sd_io_ch;
   701 
   702     io_status = g_io_channel_set_encoding (io_channel, NULL, &error);
   703     if (io_status == G_IO_STATUS_NORMAL)
   704         gmyth_debug ("[%s] Setting encoding to binary data socket).\n",
   705                      __FUNCTION__);
   706 
   707     io_cond = g_io_channel_get_buffer_condition (io_channel);
   708 
   709     io_cond_control = g_io_channel_get_buffer_condition (io_channel);
   710     if (priv->sock == NULL || (io_status == G_IO_STATUS_ERROR))
   711     {
   712         g_printerr ("gmyth_file_transfer_read(): Called with no raw socket.\n");
   713         return GMYTH_FILE_READ_ERROR;
   714     }
   715 
   716     if (priv->control_sock == NULL || (io_status_control == G_IO_STATUS_ERROR))
   717     {
   718         g_printerr
   719             ("gmyth_file_transfer_read(): Called with no control socket.\n");
   720         return GMYTH_FILE_READ_ERROR;
   721     }
   722 
   723     query = g_string_new (GMYTHTV_QUERY_HEADER);
   724     g_string_append_printf (query, "%d", priv->file_id);
   725     gmyth_debug ("[%s] Transfer_query = %s\n", __FUNCTION__, query->str);
   726 
   727     _control_acquire_context (transfer, TRUE);
   728     //Do Read
   729     gmyth_string_list_append_char_array (strlist, query->str);
   730     gmyth_string_list_append_char_array (strlist, "REQUEST_BLOCK");
   731     gmyth_string_list_append_int (strlist, size - total_read);
   732     
   733     /* 200??? Is this a TV or a HQ data traffic? */
   734     guint iter_count = 4;
   735 
   736     do
   737     {
   738         bytes_sent = 0;
   739 
   740         // Request the block to the backend
   741         gmyth_socket_write_stringlist (priv->control_sock, strlist);
   742 
   743         if (ret_strlist != NULL)
   744             g_object_unref (ret_strlist);
   745 
   746         ret_strlist = gmyth_string_list_new ();
   747         // Receives the backand answer    
   748         gmyth_socket_read_stringlist (priv->control_sock, ret_strlist);
   749 
   750         if (ret_strlist != NULL && gmyth_string_list_length (ret_strlist) > 0)
   751         {
   752             bytes_sent = gmyth_string_list_get_int (ret_strlist, 0);    // -1 on backend error
   753             gmyth_debug ("[%s] got SENT buffer message = %d\n", __FUNCTION__,
   754                          bytes_sent);
   755         }
   756 
   757         if (read_unlimited && (bytes_sent == 0))
   758         {
   759             g_usleep (300);
   760         }
   761 
   762 	--iter_count;
   763 
   764     }
   765     while (read_unlimited && (bytes_sent == 0) && iter_count > 0);
   766 
   767     if (bytes_sent > 0)
   768     {
   769         gchar *data_buffer = g_new0 (gchar, bytes_sent);
   770         io_status = g_io_channel_read_chars (io_channel,
   771                                              data_buffer,
   772                                              (gsize) bytes_sent,
   773                                              &bytes_read, &error);
   774 
   775         if (io_status != G_IO_STATUS_NORMAL)
   776         {
   777             gmyth_debug ("Error on io_channel");
   778             g_free (data_buffer);
   779             g_object_unref (strlist);
   780             retval = GMYTH_FILE_READ_ERROR;
   781             goto error;
   782         }
   783 
   784         /* append new data to the increasing byte array */
   785         data =
   786             g_byte_array_append (data, (const guint8 *) data_buffer,
   787                                  bytes_read);
   788         gmyth_file_set_offset (GMYTH_FILE (transfer),
   789                                gmyth_file_get_offset (GMYTH_FILE (transfer)) +
   790                                bytes_read);
   791 
   792         if (!read_unlimited
   793             && (gmyth_file_get_filesize (GMYTH_FILE (transfer)) > 0)
   794             && (gmyth_file_get_offset (GMYTH_FILE (transfer)) ==
   795                 gmyth_file_get_filesize (GMYTH_FILE (transfer))))
   796         {
   797             retval = GMYTH_FILE_READ_EOF;
   798             goto error;
   799         }
   800 
   801         g_free (data_buffer);
   802     }
   803     else
   804     {
   805         retval = GMYTH_FILE_READ_ERROR;
   806     }
   807 
   808     if (strlist != NULL)
   809     {
   810         g_object_unref (strlist);
   811         strlist = NULL;
   812     }
   813 
   814     if (ret_strlist != NULL)
   815     {
   816         g_object_unref (ret_strlist);
   817         ret_strlist = NULL;
   818     }
   819 
   820     if (read_unlimited && (bytes_sent == 0))
   821     {
   822         gmyth_debug ("Trying to move to the next program chain...");
   823         if (priv->recorder != NULL && priv->do_next_program_chain)
   824         {
   825 	    priv->do_next_program_chain = FALSE;
   826             retval = GMYTH_FILE_READ_NEXT_PROG_CHAIN;
   827             GMythProgramInfo *prog_info =
   828                 gmyth_recorder_get_current_program_info (priv->recorder);
   829 
   830             if (prog_info != NULL && prog_info->pathname != NULL
   831                 && strlen (prog_info->pathname->str) > 0
   832                 && g_ascii_strcasecmp (prog_info->pathname->str,
   833                                        gmyth_file_get_file_name (GMYTH_FILE
   834                                                                  (transfer))) !=
   835                 0)
   836                 ret =
   837                     gmyth_file_transfer_open (transfer,
   838                                               g_strrstr (prog_info->pathname->
   839                                                          str, "/"));
   840 
   841             if (prog_info != NULL)
   842                 g_object_unref (prog_info);
   843 
   844             if (!ret)
   845                 gmyth_debug ("Cannot change to the next program info!");
   846             else
   847                 gmyth_debug ("OK!!! MOVED to the next program info [%s]!",
   848                              gmyth_file_get_file_name (GMYTH_FILE (transfer)));
   849         }
   850 
   851     }                           /* if */
   852 
   853   error:
   854 
   855     _control_release_context (transfer);
   856     g_string_free (query, TRUE);
   857 
   858     if (error != NULL)
   859     {
   860         gmyth_debug ("Cleaning-up ERROR: %s [msg = %s, code = %d]\n",
   861                      __FUNCTION__, error->message, error->code);
   862         g_error_free (error);
   863     }
   864 
   865     if (total_read > 0)
   866         gmyth_file_set_offset (GMYTH_FILE (transfer),
   867                                gmyth_file_get_offset (GMYTH_FILE (transfer)) +
   868                                total_read);
   869 
   870     return retval;
   871 }
   872 
   873 static void
   874 _file_transfer_program_info_changed (GMythFileTransfer * transfer,
   875                                      gint msg_code, gpointer livetv_recorder)
   876 {
   877     GMythRecorder *recorder;
   878     GMythFileTransferPrivate *priv;
   879 
   880     g_return_if_fail (transfer != NULL);
   881 
   882     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   883 
   884     recorder = GMYTH_RECORDER (livetv_recorder);
   885     gmyth_debug
   886         ("Program info changed! ( file transfer orig. = %p, ptr. = [%s] )",
   887          transfer, livetv_recorder != NULL ? "[NOT NULL]" : "[NULL]");
   888 
   889     if (NULL != recorder)
   890     {
   891         gmyth_debug
   892             ("YES, the requested program info movement on the LiveTV transfer is authentical!");
   893     }
   894 
   895     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   896     g_object_ref (recorder);
   897     priv->recorder = recorder;
   898     priv->do_next_program_chain = TRUE;
   899 }
   900 
   901 /** 
   902  * Sets the timeout flag of a file being transfered.
   903  * 
   904  * @param transfer The actual File Transfer instance.
   905  * @param fast If this is <code>true</code>, sets the remote timeout to be as fast
   906  * 						as possible.
   907  * 
   908  * @return <code>true</code>, if the acquire had been got. 
   909  */
   910 gboolean
   911 gmyth_file_transfer_settimeout (GMythFileTransfer * transfer, gboolean fast)
   912 {
   913     GMythFileTransferPrivate *priv;
   914     GMythStringList *strlist = NULL;
   915 
   916     g_return_val_if_fail (transfer != NULL, FALSE);
   917 
   918     priv = GMYTH_FILE_TRANSFER_GET_PRIVATE (transfer);
   919 
   920     g_return_val_if_fail (priv->sock != NULL, FALSE);
   921     g_return_val_if_fail (priv->control_sock != NULL, FALSE);
   922 
   923     _control_acquire_context (transfer, TRUE);
   924 
   925     strlist = gmyth_string_list_new ();
   926     gmyth_string_list_append_char_array (strlist, GMYTHTV_QUERY_HEADER);
   927     gmyth_string_list_append_char_array (strlist, "SET_TIMEOUT");
   928     gmyth_string_list_append_int (strlist, fast);
   929 
   930     gint strlist_len = gmyth_socket_sendreceive_stringlist (priv->control_sock,
   931                                                             strlist);
   932 
   933     if (strlist_len > 0)
   934         gmyth_debug ("Yes, timeout was changed: %s.",
   935                      gmyth_string_list_get_char_array (strlist, 0));
   936     else
   937         gmyth_debug ("Timeout cannot be changed!");
   938 
   939     _control_release_context (transfer);
   940 
   941     gmyth_debug ("%s setting timeout flag of this file transfer = %s\n",
   942                  strlist_len > 0 ? "Yes," : "NOT", fast ? "FAST" : "NOT FAST");
   943 
   944     g_object_unref (strlist);
   945 
   946     return TRUE;
   947 }
   948 
   949 /** 
   950  * Gets the actual file size of the binary content.
   951  * 
   952  * @param transfer The actual File Transfer instance.
   953  * 
   954  * @return The actual file size in bytes. 
   955  */
   956 guint64
   957 gmyth_file_transfer_get_filesize (GMythFileTransfer * transfer)
   958 {
   959     guint64 filesize;
   960 
   961     g_return_val_if_fail (transfer != NULL, 0);
   962 
   963     g_object_get (GMYTH_FILE (transfer), "file-size", &filesize, NULL);
   964 
   965     return filesize;
   966 }