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