gmyth/src/gmyth_socket.c
author renatofilho
Tue Aug 07 16:00:49 2007 +0100 (2007-08-07)
branchtrunk
changeset 797 7563dc4ed8d5
parent 750 312d6bc514f3
child 817 888b9724f601
permissions -rw-r--r--
[svn r803] copyright fixed
leo_sobral@1
     1
/**
leo_sobral@1
     2
 * GMyth Library
leo_sobral@1
     3
 *
leo_sobral@1
     4
 * @file gmyth/gmyth_socket.c
leo_sobral@1
     5
 * 
leo_sobral@1
     6
 * @brief <p> MythTV socket implementation, according to the MythTV Project
leo_sobral@1
     7
 * (www.mythtv.org). 
leo_sobral@1
     8
 * 
leo_sobral@1
     9
 * This component provides basic socket functionalities to interact with
leo_sobral@1
    10
 * the Mythtv backend.
leo_sobral@1
    11
 * <p>
leo_sobral@1
    12
 *
leo_sobral@1
    13
 * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
leo_sobral@1
    14
 * @author Rosfran Lins Borges <rosfran.borges@indt.org.br> 
leo_sobral@1
    15
 *
rosfran@701
    16
 * 
rosfran@701
    17
 * This program is free software; you can redistribute it and/or modify
rosfran@701
    18
 * it under the terms of the GNU Lesser General Public License as published by
rosfran@701
    19
 * the Free Software Foundation; either version 2 of the License, or
rosfran@701
    20
 * (at your option) any later version.
rosfran@701
    21
 *
rosfran@701
    22
 * This program is distributed in the hope that it will be useful,
rosfran@701
    23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
rosfran@701
    24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
rosfran@701
    25
 * GNU General Public License for more details.
rosfran@701
    26
 *
rosfran@701
    27
 * You should have received a copy of the GNU Lesser General Public License
rosfran@701
    28
 * along with this program; if not, write to the Free Software
rosfran@701
    29
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
rosfran@701
    30
 */
leo_sobral@1
    31
rosfran@105
    32
#ifdef HAVE_CONFIG_H
leo_sobral@213
    33
#include "config.h"
rosfran@105
    34
#endif
rosfran@105
    35
leo_sobral@213
    36
#include "gmyth_socket.h"
leo_sobral@213
    37
rosfran@698
    38
#include <glib.h>
leo_sobral@1
    39
#include <glib/gprintf.h>
leo_sobral@1
    40
leo_sobral@1
    41
#include <arpa/inet.h>
leo_sobral@1
    42
#include <sys/types.h>
leo_sobral@1
    43
#include <sys/socket.h>
rosfran@225
    44
#include <sys/param.h>
leo_sobral@1
    45
#include <netdb.h>
rosfran@104
    46
#include <net/if.h>
leo_sobral@1
    47
#include <errno.h>
leo_sobral@446
    48
#include <assert.h>
leo_sobral@1
    49
#include <stdlib.h>
leo_sobral@1
    50
rosfran@105
    51
#include <unistd.h>
rosfran@105
    52
#include <netinet/in.h>
rosfran@105
    53
#include <fcntl.h>
rosfran@105
    54
#include <signal.h>
rosfran@105
    55
melunko@313
    56
#include <sys/ioctl.h>
rosfran@104
    57
leo_sobral@1
    58
#include "gmyth_stringlist.h"
rosfran@104
    59
#include "gmyth_uri.h"
renatofilho@131
    60
#include "gmyth_debug.h"
leo_sobral@1
    61
melunko@313
    62
#define BUFLEN 				   	512
melunko@313
    63
#define MYTH_SEPARATOR 			    	"[]:[]"
rosfran@101
    64
#define MYTH_PROTOCOL_FIELD_SIZE		8
rosfran@101
    65
renatofilho@754
    66
/*
renatofilho@754
    67
 * max number of iterations 
renatofilho@754
    68
 */
rosfran@105
    69
#define MYTHTV_MAX_VERSION_CHECKS		40
leo_sobral@1
    70
melunko@117
    71
// FIXME: put this in the right place
rosfran@343
    72
#define  MYTHTV_VERSION_DEFAULT			31
melunko@117
    73
renatofilho@754
    74
/*
renatofilho@754
    75
 * static GStaticMutex mutex = G_STATIC_MUTEX_INIT; 
renatofilho@754
    76
 */
leo_sobral@1
    77
renatofilho@754
    78
/*
renatofilho@754
    79
 * static GStaticRWLock rwlock = G_STATIC_RW_LOCK_INIT;
renatofilho@754
    80
 */
rosfran@336
    81
renatofilho@754
    82
static gchar   *local_hostname = NULL;
rosfran@177
    83
renatofilho@754
    84
static void     gmyth_socket_class_init(GMythSocketClass * klass);
renatofilho@754
    85
static void     gmyth_socket_init(GMythSocket * object);
leo_sobral@1
    86
renatofilho@754
    87
static void     gmyth_socket_dispose(GObject * object);
renatofilho@754
    88
static void     gmyth_socket_finalize(GObject * object);
leo_sobral@1
    89
renatofilho@750
    90
G_DEFINE_TYPE(GMythSocket, gmyth_socket, G_TYPE_OBJECT)
renatofilho@754
    91
    static void     gmyth_socket_class_init(GMythSocketClass * klass)
leo_sobral@1
    92
{
renatofilho@754
    93
    GObjectClass   *gobject_class;
leo_sobral@1
    94
renatofilho@754
    95
    gobject_class = (GObjectClass *) klass;
leo_sobral@1
    96
renatofilho@754
    97
    gobject_class->dispose = gmyth_socket_dispose;
renatofilho@754
    98
    gobject_class->finalize = gmyth_socket_finalize;
leo_sobral@1
    99
}
leo_sobral@1
   100
leo_sobral@1
   101
static void
renatofilho@750
   102
gmyth_socket_init(GMythSocket * gmyth_socket)
leo_sobral@1
   103
{
rosfran@698
   104
renatofilho@754
   105
    /*
renatofilho@754
   106
     * gmyth_socket->local_hostname = NULL; 
renatofilho@754
   107
     */
rosfran@698
   108
leo_sobral@1
   109
}
leo_sobral@1
   110
leo_sobral@1
   111
/** Gets the some important address translation info, from the client socket
leo_sobral@1
   112
 * that will open a connection.
leo_sobral@1
   113
 * 
leo_sobral@1
   114
 * @return gint that represents the error number from getaddrinfo(). 
leo_sobral@1
   115
 */
renatofilho@754
   116
static          gint
renatofilho@750
   117
gmyth_socket_toaddrinfo(const gchar * addr, gint port,
renatofilho@754
   118
                        struct addrinfo **addrInfo)
leo_sobral@1
   119
{
renatofilho@754
   120
    struct addrinfo hints;
renatofilho@754
   121
    gchar          *portStr = NULL;
renatofilho@754
   122
    gint            errorn = EADDRNOTAVAIL;
rosfran@154
   123
renatofilho@754
   124
    g_return_val_if_fail(addr != NULL, -1);
rosfran@698
   125
renatofilho@754
   126
    memset(&hints, 0, sizeof(struct addrinfo));
renatofilho@754
   127
    hints.ai_family = AF_INET;
renatofilho@754
   128
    hints.ai_socktype = SOCK_STREAM;
renatofilho@754
   129
    /*
renatofilho@754
   130
     * hints.ai_flags = AI_NUMERICHOST; 
renatofilho@754
   131
     */
rosfran@698
   132
renatofilho@754
   133
    if (port != -1)
renatofilho@754
   134
        portStr = g_strdup_printf("%d", port);
renatofilho@754
   135
    else
renatofilho@754
   136
        portStr = NULL;
melunko@313
   137
renatofilho@754
   138
    gmyth_debug("Getting name resolution for: %s, %d\n", addr, port);
leo_sobral@1
   139
renatofilho@754
   140
    if ((errorn = getaddrinfo(addr, portStr, &hints, addrInfo)) != 0) {
renatofilho@754
   141
        gmyth_debug("[%s] Socket ERROR: %s\n", __FUNCTION__,
renatofilho@754
   142
                    gai_strerror(errorn));
renatofilho@754
   143
    }
leo_sobral@446
   144
renatofilho@754
   145
    g_free(portStr);
leo_sobral@446
   146
renatofilho@754
   147
    return errorn;
leo_sobral@1
   148
}
leo_sobral@1
   149
renatofilho@464
   150
/*
renatofilho@754
   151
 * static gint gmyth_socket_find_match_address_uri( GMythURI* uri, gchar
renatofilho@754
   152
 * *address ) { if ( g_ascii_strcasecmp( gmyth_uri_get_host( uri ),
renatofilho@754
   153
 * address ) == 0 ) { //gmyth_debug( "Found URI: %s !!!\n",
renatofilho@754
   154
 * rui_uri_getvalue(uri) ); return 0; } else { return -1; } } 
renatofilho@754
   155
 */
rosfran@104
   156
renatofilho@754
   157
const gchar    *PATH_PROC_NET_DEV = "/proc/net/dev";
rosfran@104
   158
rosfran@105
   159
/** Gets the list of all local network interfaces (using the /proc/net/dev directory).
rosfran@105
   160
 * 
rosfran@105
   161
 * @param current_connections	A list with all the network interfaces are valid, 
rosfran@105
   162
 * 		to be applied just like a filter.
rosfran@105
   163
 * @return List with all the local net interfaces. 
rosfran@105
   164
 */
renatofilho@754
   165
GList          *
renatofilho@750
   166
gmyth_socket_get_local_addrs(GList * current_connections)
rosfran@104
   167
{
rosfran@104
   168
renatofilho@754
   169
    GList          *local_addrs = NULL;
renatofilho@754
   170
    FILE           *fd;
renatofilho@754
   171
    gint            s;
renatofilho@754
   172
    gchar           buffer[256 + 1];
renatofilho@754
   173
    gchar           ifaddr[20 + 1];
renatofilho@754
   174
    gchar          *ifname;
renatofilho@754
   175
    gchar          *sep;
leo_sobral@387
   176
renatofilho@754
   177
    s = socket(AF_INET, SOCK_DGRAM, 0);
renatofilho@754
   178
    if (s < 0)
renatofilho@754
   179
        return 0;
renatofilho@754
   180
    fd = fopen(PATH_PROC_NET_DEV, "r");
renatofilho@754
   181
    fgets(buffer, sizeof(buffer) - 1, fd);
renatofilho@754
   182
    fgets(buffer, sizeof(buffer) - 1, fd);
renatofilho@754
   183
    while (!feof(fd)) {
renatofilho@754
   184
        ifname = buffer;
rosfran@104
   185
renatofilho@754
   186
        if (fgets(buffer, sizeof(buffer) - 1, fd) == NULL)
renatofilho@754
   187
            break;
renatofilho@754
   188
        sep = strrchr(buffer, ':');
renatofilho@754
   189
        if (sep)
renatofilho@754
   190
            *sep = 0;
renatofilho@754
   191
        while (*ifname == ' ')
renatofilho@754
   192
            ifname++;
renatofilho@754
   193
        struct ifreq    req;
rosfran@698
   194
renatofilho@754
   195
        strcpy(req.ifr_name, ifname);
renatofilho@754
   196
        if (ioctl(s, SIOCGIFFLAGS, &req) < 0)
renatofilho@754
   197
            continue;
renatofilho@754
   198
        if (!(req.ifr_flags & IFF_UP))
renatofilho@754
   199
            continue;
renatofilho@754
   200
        if (req.ifr_flags & IFF_LOOPBACK)
renatofilho@754
   201
            continue;
renatofilho@754
   202
        if (ioctl(s, SIOCGIFADDR, &req) < 0)
renatofilho@754
   203
            continue;
renatofilho@754
   204
        g_strlcpy(ifaddr,
renatofilho@754
   205
                  inet_ntoa(((struct sockaddr_in *) &req.ifr_addr)->
renatofilho@754
   206
                            sin_addr), sizeof(struct ifaddr) - 1);
renatofilho@754
   207
        local_addrs = g_list_append(local_addrs, g_strdup(ifaddr));
rosfran@698
   208
renatofilho@754
   209
        gmyth_debug
renatofilho@754
   210
            ("( from the /proc/net/dev) Interface name: %s, address: %s\n",
renatofilho@754
   211
             ifname, ifaddr);
renatofilho@754
   212
    }
renatofilho@754
   213
    fclose(fd);
renatofilho@754
   214
    close(s);
renatofilho@754
   215
    return local_addrs;
rosfran@104
   216
}
rosfran@104
   217
rosfran@104
   218
/**
rosfran@104
   219
 * Get only the local addresses from the primary interface
rosfran@104
   220
 */
renatofilho@754
   221
gchar          *
renatofilho@750
   222
gmyth_socket_get_primary_addr(void)
rosfran@104
   223
{
renatofilho@754
   224
    gchar          *if_eth0 = g_new0(gchar, sizeof(struct ifaddr) - 1);
renatofilho@754
   225
    GList          *if_tmp = NULL;
rosfran@104
   226
renatofilho@754
   227
    GList          *interfs = gmyth_socket_get_local_addrs(NULL);
rosfran@698
   228
renatofilho@754
   229
    if (interfs != NULL && (g_list_length(interfs) > 0)) {
renatofilho@754
   230
        // get the first occurrence (primary interface) 
renatofilho@754
   231
        if_tmp = g_list_first(interfs);
rosfran@698
   232
renatofilho@754
   233
        if (if_tmp != NULL)
renatofilho@754
   234
            g_strlcpy(if_eth0, (gchar *) if_tmp->data,
renatofilho@754
   235
                      sizeof(struct ifaddr) - 1);
rosfran@698
   236
renatofilho@754
   237
    }
rosfran@698
   238
renatofilho@754
   239
    if (interfs != NULL)
renatofilho@754
   240
        g_list_free(interfs);
rosfran@698
   241
renatofilho@754
   242
    return if_eth0;
rosfran@104
   243
}
rosfran@104
   244
leo_sobral@1
   245
/** This function retrieves the local hostname of the 
leo_sobral@1
   246
 * client machine.
leo_sobral@1
   247
 *
leo_sobral@1
   248
 * @return GString* get local hostname.
leo_sobral@1
   249
 */
renatofilho@754
   250
GString        *
renatofilho@750
   251
gmyth_socket_get_local_hostname(void)
leo_sobral@1
   252
{
renatofilho@754
   253
    char            hname[50];
renatofilho@754
   254
    gint            res = gethostname(hname, 50);
melunko@313
   255
renatofilho@754
   256
    if (res == -1) {
renatofilho@754
   257
        gmyth_debug("Error while getting hostname");
renatofilho@754
   258
        return g_string_new("default");
renatofilho@754
   259
    }
melunko@313
   260
renatofilho@754
   261
    return g_string_new(hname);
melunko@663
   262
rosfran@698
   263
#if 0
renatofilho@754
   264
    GString        *str = NULL;
leo_sobral@1
   265
renatofilho@754
   266
    if (local_hostname != NULL && strlen(local_hostname) > 0)
renatofilho@754
   267
        return g_string_new(local_hostname);
rosfran@698
   268
renatofilho@754
   269
    gchar          *localaddr = NULL;
renatofilho@754
   270
    gboolean        found_addr = FALSE;
renatofilho@754
   271
    struct addrinfo *addr_info_data = NULL,
renatofilho@754
   272
        *addr_info0 = NULL;
renatofilho@754
   273
    struct sockaddr_in *sa = NULL;
renatofilho@754
   274
    gchar           localhostname[MAXHOSTNAMELEN];
leo_sobral@1
   275
melunko@313
   276
renatofilho@754
   277
    if (gethostname(localhostname, MAXHOSTNAMELEN) != 0) {
renatofilho@754
   278
        gmyth_debug("Error on gethostname");
renatofilho@754
   279
    }
renatofilho@754
   280
    localhostname[MAXHOSTNAMELEN - 1] = 0;
rosfran@225
   281
renatofilho@754
   282
    gint            err =
renatofilho@754
   283
        gmyth_socket_toaddrinfo(localhostname, -1, &addr_info_data);
rosfran@698
   284
renatofilho@754
   285
    if (err == EADDRNOTAVAIL) {
renatofilho@754
   286
        gmyth_debug("[%s] Address (%s) not available. (reason = %d)\n",
renatofilho@754
   287
                    __FUNCTION__, localhostname, err);
renatofilho@754
   288
        return str;
renatofilho@754
   289
    }
rosfran@698
   290
renatofilho@754
   291
    g_mutex_lock(gmyth_socket->mutex);
leo_sobral@1
   292
renatofilho@754
   293
    addr_info0 = addr_info_data;
leo_sobral@1
   294
renatofilho@754
   295
    while (addr_info0 != NULL && addr_info0->ai_addr != NULL &&
renatofilho@754
   296
           (sa = (struct sockaddr_in *) addr_info0->ai_addr) != NULL
renatofilho@754
   297
           && !found_addr) {
renatofilho@754
   298
        localaddr = inet_ntoa(sa->sin_addr);
leo_sobral@1
   299
renatofilho@754
   300
        if (localaddr != NULL && (g_strrstr(localaddr, "127") == NULL)) {
renatofilho@754
   301
            str = g_string_new(localaddr);
renatofilho@754
   302
            found_addr = TRUE;
renatofilho@754
   303
            g_free(localaddr);
renatofilho@754
   304
            break;
renatofilho@754
   305
        }
renatofilho@754
   306
        /*
renatofilho@754
   307
         * if (localaddr != NULL) { g_free (localaddr); localaddr = NULL;
renatofilho@754
   308
         * } 
renatofilho@754
   309
         */
rosfran@225
   310
renatofilho@754
   311
        addr_info0 = addr_info0->ai_next;
renatofilho@754
   312
    };
rosfran@698
   313
renatofilho@754
   314
    freeaddrinfo(addr_info_data);
renatofilho@754
   315
    addr_info_data = NULL;
rosfran@105
   316
renatofilho@754
   317
    if (found_addr == FALSE) {
renatofilho@754
   318
        gchar          *prim_addr = gmyth_socket_get_primary_addr();
rosfran@698
   319
renatofilho@754
   320
        if (prim_addr != NULL) {
renatofilho@754
   321
            gmyth_debug
renatofilho@754
   322
                ("[%s] Could not determine the local alphanumerical hostname. Setting to %s\n",
renatofilho@754
   323
                 __FUNCTION__, prim_addr);
rosfran@698
   324
renatofilho@754
   325
            str = g_string_new(prim_addr);
renatofilho@754
   326
            g_free(prim_addr);
renatofilho@754
   327
        } else {
renatofilho@754
   328
            str = g_string_new(localhostname);
renatofilho@754
   329
        }
renatofilho@754
   330
    }
leo_sobral@1
   331
renatofilho@754
   332
    g_mutex_unlock(gmyth_socket->mutex);
rosfran@698
   333
renatofilho@754
   334
    if (str != NULL && str->str != NULL)
renatofilho@754
   335
        local_hostname = g_strdup(str->str);
rosfran@225
   336
renatofilho@754
   337
    return str;
rosfran@698
   338
#endif
leo_sobral@1
   339
}
leo_sobral@1
   340
leo_sobral@1
   341
static void
renatofilho@750
   342
gmyth_socket_dispose(GObject * object)
leo_sobral@1
   343
{
renatofilho@754
   344
    GMythSocket    *gmyth_socket = GMYTH_SOCKET(object);
leo_sobral@1
   345
renatofilho@754
   346
    /*
renatofilho@754
   347
     * disconnect socket 
renatofilho@754
   348
     */
renatofilho@754
   349
    gmyth_socket_close_connection(gmyth_socket);
rosfran@698
   350
renatofilho@754
   351
    g_free(gmyth_socket->hostname);
rosfran@698
   352
renatofilho@754
   353
    g_free(local_hostname);
rosfran@698
   354
renatofilho@754
   355
    local_hostname = NULL;
rosfran@698
   356
renatofilho@754
   357
    if (gmyth_socket->mutex != NULL) {
renatofilho@754
   358
        g_mutex_free(gmyth_socket->mutex);
renatofilho@754
   359
        gmyth_socket->mutex = NULL;
renatofilho@754
   360
    }
renatofilho@147
   361
renatofilho@754
   362
    G_OBJECT_CLASS(gmyth_socket_parent_class)->dispose(object);
leo_sobral@1
   363
}
leo_sobral@1
   364
leo_sobral@1
   365
static void
renatofilho@750
   366
gmyth_socket_finalize(GObject * object)
leo_sobral@1
   367
{
renatofilho@754
   368
    g_signal_handlers_destroy(object);
leo_sobral@1
   369
renatofilho@754
   370
    G_OBJECT_CLASS(gmyth_socket_parent_class)->finalize(object);
leo_sobral@1
   371
}
leo_sobral@1
   372
leo_sobral@1
   373
/** Creates a new instance of GMythSocket.
leo_sobral@1
   374
 * 
leo_sobral@1
   375
 * @return a new instance of GMythSocket.
leo_sobral@1
   376
 */
renatofilho@754
   377
GMythSocket    *
renatofilho@750
   378
gmyth_socket_new()
leo_sobral@1
   379
{
renatofilho@754
   380
    GMythSocket    *gmyth_socket =
renatofilho@754
   381
        GMYTH_SOCKET(g_object_new(GMYTH_SOCKET_TYPE, NULL));
leo_sobral@1
   382
renatofilho@754
   383
    gmyth_socket->mythtv_version = MYTHTV_VERSION_DEFAULT;
rosfran@698
   384
renatofilho@754
   385
    gmyth_socket->mutex = g_mutex_new();
leo_sobral@1
   386
renatofilho@754
   387
    return gmyth_socket;
leo_sobral@1
   388
}
leo_sobral@1
   389
rosfran@105
   390
/** Try to open an asynchronous connection to the MythTV backend.
rosfran@105
   391
 * 
leo_sobral@446
   392
 * @param fd 		Socket descriptor.
rosfran@105
   393
 * @param remote 	Remote address.
rosfran@105
   394
 * @param len 		Newly created socket length field.
rosfran@105
   395
 * @param timeout	Timeval argument with the time interval to timeout before closing.
leo_sobral@446
   396
 * @param err		Error message number.
rosfran@105
   397
 * @return Any numerical value below 0, if an error had been found.
rosfran@105
   398
 */
renatofilho@754
   399
static          gint
renatofilho@750
   400
gmyth_socket_try_connect(gint fd, struct sockaddr *remote, gint len,
renatofilho@754
   401
                         struct timeval *timeout, gint * err)
rosfran@105
   402
{
renatofilho@754
   403
    /*
renatofilho@754
   404
     * g_return_val_if_fail( timeout != NULL, 0 ); 
renatofilho@754
   405
     */
renatofilho@754
   406
    gint            saveflags,
renatofilho@754
   407
                    ret,
renatofilho@754
   408
                    back_err;
rosfran@698
   409
renatofilho@754
   410
    fd_set          fd_w;
rosfran@698
   411
renatofilho@754
   412
    saveflags = fcntl(fd, F_GETFL, 0);
renatofilho@754
   413
    if (saveflags < 0) {
renatofilho@754
   414
        gmyth_debug("[%s] Problems when getting socket flags on fcntl.\n",
renatofilho@754
   415
                    __FUNCTION__);
renatofilho@754
   416
        *err = errno;
renatofilho@754
   417
        return -1;
renatofilho@754
   418
    }
rosfran@698
   419
renatofilho@754
   420
    /*
renatofilho@754
   421
     * Set non blocking 
renatofilho@754
   422
     */
renatofilho@754
   423
    if (fcntl(fd, F_SETFL, saveflags | O_NONBLOCK) < 0) {
renatofilho@754
   424
        gmyth_debug
renatofilho@754
   425
            ("[%s] Problems when setting non-blocking using fcntl.\n",
renatofilho@754
   426
             __FUNCTION__);
renatofilho@754
   427
        *err = errno;
renatofilho@754
   428
        return -1;
renatofilho@754
   429
    }
rosfran@698
   430
renatofilho@754
   431
    /*
renatofilho@754
   432
     * This will return immediately 
renatofilho@754
   433
     */
renatofilho@754
   434
    *err = connect(fd, remote, len);
renatofilho@754
   435
    back_err = errno;
rosfran@698
   436
renatofilho@754
   437
    /*
renatofilho@754
   438
     * restore flags 
renatofilho@754
   439
     */
renatofilho@754
   440
    if (fcntl(fd, F_SETFL, saveflags) < 0) {
renatofilho@754
   441
        gmyth_debug
renatofilho@754
   442
            ("[%s] Problems when trying to restore flags with fcntl.\n",
renatofilho@754
   443
             __FUNCTION__);
renatofilho@754
   444
        *err = errno;
renatofilho@754
   445
        return -1;
renatofilho@754
   446
    }
rosfran@698
   447
renatofilho@754
   448
    /*
renatofilho@754
   449
     * return unless the connection was successful or the connect is still 
renatofilho@754
   450
     * in progress. 
renatofilho@754
   451
     */
renatofilho@754
   452
    if (*err < 0 && back_err != EINPROGRESS) {
renatofilho@754
   453
        gmyth_debug
renatofilho@754
   454
            ("[%s] Connection unsucessfully (it is not in progress).\n",
renatofilho@754
   455
             __FUNCTION__);
renatofilho@754
   456
        *err = errno;
renatofilho@754
   457
        return -1;
renatofilho@754
   458
    }
rosfran@698
   459
renatofilho@754
   460
    FD_ZERO(&fd_w);
renatofilho@754
   461
    FD_SET(fd, &fd_w);
rosfran@698
   462
renatofilho@754
   463
    *err = select(FD_SETSIZE, NULL, &fd_w, NULL, timeout);
renatofilho@754
   464
    if (*err < 0) {
renatofilho@754
   465
        gmyth_debug("[%s] Connection unsucessfull (timed out).\n",
renatofilho@754
   466
                    __FUNCTION__);
renatofilho@754
   467
        *err = errno;
renatofilho@754
   468
        return -1;
renatofilho@754
   469
    }
rosfran@698
   470
renatofilho@754
   471
    /*
renatofilho@754
   472
     * 0 means it timeout out & no fds changed 
renatofilho@754
   473
     */
renatofilho@754
   474
    if (*err == 0) {
renatofilho@754
   475
        gmyth_debug
renatofilho@754
   476
            ("[%s] Connection unsucessfull [%d] - 0 means it timeout out & no fds changed\n",
renatofilho@754
   477
             __FUNCTION__, *err);
renatofilho@754
   478
        close(fd);
renatofilho@754
   479
        *err = ETIMEDOUT;
renatofilho@754
   480
        return -1;
renatofilho@754
   481
    }
rosfran@698
   482
renatofilho@754
   483
    /*
renatofilho@754
   484
     * Get the return code from the connect 
renatofilho@754
   485
     */
renatofilho@754
   486
    len = sizeof(ret);
renatofilho@754
   487
    *err = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, (socklen_t *) & len);
rosfran@698
   488
renatofilho@754
   489
    if (*err < 0) {
renatofilho@754
   490
        gmyth_debug("[%s] Connection unsucessfull.\n", __FUNCTION__);
renatofilho@754
   491
        *err = errno;
renatofilho@754
   492
        return -1;
renatofilho@754
   493
    }
rosfran@698
   494
renatofilho@754
   495
    /*
renatofilho@754
   496
     * ret=0 means success, otherwise it contains the errno 
renatofilho@754
   497
     */
renatofilho@754
   498
    if (ret) {
renatofilho@754
   499
        gmyth_debug
renatofilho@754
   500
            ("[%s] Connection unsucessfull - Couldn't connect to remote host!!!\n",
renatofilho@754
   501
             __FUNCTION__);
renatofilho@754
   502
        *err = ret;
renatofilho@754
   503
        return -1;
renatofilho@754
   504
    }
rosfran@698
   505
renatofilho@754
   506
    *err = 0;
renatofilho@754
   507
    return 0;
rosfran@105
   508
}
rosfran@105
   509
leo_sobral@1
   510
/** Connects to the backend.
leo_sobral@1
   511
 * 
leo_sobral@1
   512
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   513
 * @param hostname The backend hostname or IP address.
leo_sobral@1
   514
 * @param port The backend port.
leo_sobral@1
   515
 * @return TRUE if success, FALSE if error.
leo_sobral@1
   516
 */
melunko@258
   517
melunko@258
   518
leo_sobral@1
   519
gboolean
renatofilho@750
   520
gmyth_socket_connect(GMythSocket * gmyth_socket,
renatofilho@754
   521
                     const gchar * hostname, gint port)
leo_sobral@1
   522
{
renatofilho@754
   523
    return gmyth_socket_connect_with_timeout(gmyth_socket, hostname, port,
renatofilho@754
   524
                                             0);
melunko@258
   525
}
melunko@258
   526
melunko@258
   527
gboolean
renatofilho@750
   528
gmyth_socket_connect_with_timeout(GMythSocket * gmyth_socket,
renatofilho@754
   529
                                  const gchar * hostname, gint port,
renatofilho@754
   530
                                  guint timeout)
melunko@258
   531
{
renatofilho@754
   532
    struct addrinfo *addr_info_data = NULL,
renatofilho@754
   533
        *addr_info0 = NULL;
renatofilho@754
   534
    struct linger   ling;
renatofilho@754
   535
    gchar          *tmp_str;
renatofilho@754
   536
    gint            ret_code = 0;   /* -1 */
rosfran@698
   537
renatofilho@754
   538
    /*
renatofilho@754
   539
     * FIXME: add as function parameter 
renatofilho@754
   540
     */
renatofilho@754
   541
    gint            err;
renatofilho@754
   542
    gint            errno;
renatofilho@754
   543
    gboolean        ret = TRUE;
leo_sobral@1
   544
renatofilho@754
   545
    gmyth_debug("CONNECTING %s:%d", hostname, port);
rosfran@225
   546
renatofilho@754
   547
    if (hostname == NULL)
renatofilho@754
   548
        gmyth_debug("Invalid hostname parameter!\n");
leo_sobral@1
   549
renatofilho@754
   550
    /*
renatofilho@754
   551
     * store hostname and port number 
renatofilho@754
   552
     */
renatofilho@754
   553
    gmyth_debug("CONNECTING %s:%d", hostname, port);
renatofilho@206
   554
renatofilho@754
   555
    errno = gmyth_socket_toaddrinfo(hostname, port, &addr_info_data);
rosfran@225
   556
renatofilho@754
   557
    g_return_val_if_fail(addr_info_data != NULL
renatofilho@754
   558
                         && hostname != NULL, FALSE);
rosfran@225
   559
renatofilho@754
   560
    /*
renatofilho@754
   561
     * hack to avoid deleting the hostname when gmyth_socket->hostname ==
renatofilho@754
   562
     * hostname 
renatofilho@754
   563
     */
renatofilho@754
   564
    tmp_str = gmyth_socket->hostname;
leo_sobral@446
   565
renatofilho@754
   566
    gmyth_socket->hostname = g_strdup(hostname);
renatofilho@754
   567
    gmyth_socket->port = port;
leo_sobral@1
   568
renatofilho@754
   569
    g_free(tmp_str);
leo_sobral@446
   570
renatofilho@754
   571
    for (addr_info0 = addr_info_data; addr_info0;
renatofilho@754
   572
         addr_info0 = addr_info_data->ai_next) {
renatofilho@754
   573
        /*
renatofilho@754
   574
         * init socket descriptor 
renatofilho@754
   575
         */
renatofilho@754
   576
        gmyth_socket->sd =
renatofilho@754
   577
            socket(addr_info0->ai_family, addr_info0->ai_socktype,
renatofilho@754
   578
                   addr_info0->ai_protocol);
leo_sobral@1
   579
renatofilho@754
   580
        if (gmyth_socket->sd < 0)
renatofilho@754
   581
            continue;
leo_sobral@1
   582
renatofilho@754
   583
        struct timeval *timeout_val = g_new0(struct timeval, 1);
rosfran@698
   584
renatofilho@754
   585
        if (timeout != 0) {
renatofilho@754
   586
            timeout_val->tv_sec = timeout;
renatofilho@754
   587
            timeout_val->tv_usec = 0;
renatofilho@754
   588
        } else {
renatofilho@754
   589
            timeout_val->tv_sec = 5;
renatofilho@754
   590
            timeout_val->tv_usec = 100;
renatofilho@754
   591
        }
rosfran@698
   592
renatofilho@754
   593
        if (gmyth_socket_try_connect
renatofilho@754
   594
            (gmyth_socket->sd, (struct sockaddr *) addr_info0->ai_addr,
renatofilho@754
   595
             addr_info0->ai_addrlen, timeout_val, &ret_code) < 0) {
renatofilho@754
   596
            gmyth_debug("[%s] Error connecting to backend!\n",
renatofilho@754
   597
                        __FUNCTION__);
renatofilho@754
   598
            if (ret_code == ETIMEDOUT)
renatofilho@754
   599
                gmyth_debug("[%s]\tBackend host unreachable!\n",
renatofilho@754
   600
                            __FUNCTION__);
leo_sobral@1
   601
renatofilho@754
   602
            close(gmyth_socket->sd);
renatofilho@754
   603
            gmyth_socket->sd = -1;
renatofilho@754
   604
            gmyth_debug("ERROR: %s\n", gai_strerror(ret_code));
renatofilho@754
   605
            g_free(timeout_val);
renatofilho@754
   606
            continue;
renatofilho@754
   607
        }
leo_sobral@1
   608
renatofilho@754
   609
        g_free(timeout_val);
melunko@258
   610
renatofilho@754
   611
        /*
renatofilho@754
   612
         * only will be reached if none of the error above occurred 
renatofilho@754
   613
         */
renatofilho@754
   614
        break;
renatofilho@754
   615
    }
melunko@258
   616
renatofilho@754
   617
    freeaddrinfo(addr_info_data);
renatofilho@754
   618
    addr_info_data = NULL;
leo_sobral@1
   619
renatofilho@754
   620
    if (gmyth_socket->sd_io_ch != NULL) {
renatofilho@754
   621
        g_io_channel_unref(gmyth_socket->sd_io_ch);
renatofilho@754
   622
        gmyth_socket->sd_io_ch = NULL;
renatofilho@754
   623
    }
rosfran@698
   624
rosfran@698
   625
renatofilho@754
   626
    memset(&ling, 0, sizeof(struct linger));
renatofilho@754
   627
    ling.l_onoff = TRUE;
renatofilho@754
   628
    ling.l_linger = 1;
rosfran@698
   629
renatofilho@754
   630
    err =
renatofilho@754
   631
        setsockopt(gmyth_socket->sd, SOL_SOCKET, SO_LINGER, &ling,
renatofilho@754
   632
                   sizeof(struct linger));
rosfran@698
   633
renatofilho@754
   634
    if (err < 0) {
renatofilho@754
   635
        gmyth_debug("[%s] Setting connection unsucessfull.\n",
renatofilho@754
   636
                    __FUNCTION__);
renatofilho@754
   637
        err = errno;
renatofilho@754
   638
        ret = FALSE;
renatofilho@754
   639
        goto cleanup;
renatofilho@754
   640
    }
leo_sobral@446
   641
renatofilho@754
   642
    gmyth_socket->sd_io_ch = g_io_channel_unix_new(gmyth_socket->sd);
rosfran@698
   643
renatofilho@754
   644
    g_io_channel_set_close_on_unref(gmyth_socket->sd_io_ch, TRUE);
renatofilho@754
   645
    // g_io_channel_set_encoding (gmyth_socket->sd_io_ch, NULL, NULL );
leo_sobral@1
   646
renatofilho@754
   647
    GIOFlags        flags = g_io_channel_get_flags(gmyth_socket->sd_io_ch);
leo_sobral@446
   648
renatofilho@754
   649
    /*
renatofilho@754
   650
     * unset the nonblock flag 
renatofilho@754
   651
     */
renatofilho@754
   652
    flags &= ~G_IO_FLAG_NONBLOCK;
renatofilho@754
   653
    /*
renatofilho@754
   654
     * unset the nonblocking stuff for some time, because GNUTLS doesn't
renatofilho@754
   655
     * like that 
renatofilho@754
   656
     */
renatofilho@754
   657
    g_io_channel_set_flags(gmyth_socket->sd_io_ch, flags, NULL);
rosfran@698
   658
renatofilho@754
   659
    ret = (ret_code == 0) ? TRUE : FALSE;
rosfran@698
   660
renatofilho@754
   661
  cleanup:
renatofilho@754
   662
    if (!ret)
renatofilho@754
   663
        gmyth_debug("GMythSocket error - return code error!");
rosfran@698
   664
renatofilho@754
   665
    return ret;
leo_sobral@1
   666
}
leo_sobral@1
   667
leo_sobral@1
   668
/** Gets the GIOChannel associated to the given GMythSocket.
leo_sobral@1
   669
 * 
leo_sobral@1
   670
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   671
 */
renatofilho@754
   672
GIOChannel     *
renatofilho@750
   673
gmyth_socket_get_io_channel(GMythSocket * gmyth_socket)
leo_sobral@1
   674
{
renatofilho@754
   675
    g_return_val_if_fail(gmyth_socket != NULL, NULL);
leo_sobral@1
   676
renatofilho@754
   677
    return gmyth_socket->sd_io_ch;
leo_sobral@1
   678
}
leo_sobral@1
   679
leo_sobral@1
   680
/** Verifies if the socket is able to read.
leo_sobral@1
   681
 * 
leo_sobral@1
   682
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   683
 * @return TRUE if the socket is able to read, FALSE if not.
leo_sobral@1
   684
 */
leo_sobral@1
   685
gboolean
renatofilho@750
   686
gmyth_socket_is_able_to_read(GMythSocket * gmyth_socket)
leo_sobral@1
   687
{
renatofilho@754
   688
    gboolean        ret = TRUE;
leo_sobral@1
   689
renatofilho@754
   690
    /*
renatofilho@754
   691
     * verify if the input (read) buffer is ready to receive data 
renatofilho@754
   692
     */
renatofilho@754
   693
    GIOCondition    io_cond =
renatofilho@754
   694
        g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
leo_sobral@1
   695
renatofilho@754
   696
    if ((io_cond & G_IO_IN) == 0) {
renatofilho@754
   697
        gmyth_debug("[%s] IO channel is not able to send data!\n",
renatofilho@754
   698
                    __FUNCTION__);
renatofilho@754
   699
        ret = FALSE;
renatofilho@754
   700
    }
leo_sobral@1
   701
renatofilho@754
   702
    return ret;
leo_sobral@1
   703
leo_sobral@1
   704
}
leo_sobral@1
   705
leo_sobral@1
   706
/** Verifies if the socket is able to write.
leo_sobral@1
   707
 * 
leo_sobral@1
   708
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   709
 * @return TRUE if the socket is able to write, FALSE if not.
leo_sobral@1
   710
 */
leo_sobral@1
   711
gboolean
renatofilho@750
   712
gmyth_socket_is_able_to_write(GMythSocket * gmyth_socket)
leo_sobral@1
   713
{
renatofilho@754
   714
    gboolean        ret = TRUE;
leo_sobral@1
   715
renatofilho@754
   716
    /*
renatofilho@754
   717
     * verify if the input (read) buffer is ready to receive data 
renatofilho@754
   718
     */
renatofilho@754
   719
    GIOCondition    io_cond =
renatofilho@754
   720
        g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
leo_sobral@1
   721
renatofilho@754
   722
    if (((io_cond & G_IO_OUT) == 0) || ((io_cond & G_IO_HUP) == 0)) {
renatofilho@754
   723
        gmyth_debug("[%s] IO channel is not able to send data!\n",
renatofilho@754
   724
                    __FUNCTION__);
renatofilho@754
   725
        ret = FALSE;
renatofilho@754
   726
    }
leo_sobral@1
   727
renatofilho@754
   728
    return ret;
leo_sobral@1
   729
leo_sobral@1
   730
}
leo_sobral@1
   731
leo_sobral@1
   732
/** Sends a command to the backend.
leo_sobral@1
   733
 * 
leo_sobral@1
   734
 * @param gmyth_socket the GMythSocket instance.
leo_sobral@1
   735
 * @param command The string command to be sent.
leo_sobral@1
   736
 */
leo_sobral@1
   737
gboolean
renatofilho@750
   738
gmyth_socket_send_command(GMythSocket * gmyth_socket, GString * command)
leo_sobral@1
   739
{
renatofilho@754
   740
    gboolean        ret = TRUE;
leo_sobral@1
   741
renatofilho@754
   742
    GIOStatus       io_status = G_IO_STATUS_NORMAL;
rosfran@698
   743
renatofilho@754
   744
    // GIOCondition io_cond;
renatofilho@754
   745
    GError         *error = NULL;
rosfran@698
   746
renatofilho@754
   747
    gchar          *buffer = NULL;
leo_sobral@1
   748
renatofilho@754
   749
    gsize           bytes_written = 0;
leo_sobral@1
   750
renatofilho@754
   751
    g_return_val_if_fail(gmyth_socket->sd_io_ch != NULL, FALSE);
rosfran@698
   752
renatofilho@754
   753
    if (command == NULL || (command->len <= 0) || command->str == NULL) {
renatofilho@754
   754
        gmyth_debug("[%s] Invalid NULL command parameter!\n",
renatofilho@754
   755
                    __FUNCTION__);
renatofilho@754
   756
        ret = FALSE;
renatofilho@754
   757
        goto done;
renatofilho@754
   758
    }
leo_sobral@1
   759
renatofilho@754
   760
    g_mutex_lock(gmyth_socket->mutex);
renatofilho@754
   761
    gmyth_debug("Sending command to backend: %s\n", command->str);
leo_sobral@1
   762
renatofilho@754
   763
    buffer = g_strnfill(BUFLEN, ' ');
renatofilho@754
   764
    g_snprintf(buffer, MYTH_PROTOCOL_FIELD_SIZE + 1, "%-8d", command->len);
leo_sobral@1
   765
renatofilho@754
   766
    command = g_string_prepend(command, buffer);
leo_sobral@1
   767
renatofilho@754
   768
    /*
renatofilho@754
   769
     * write bytes to socket 
renatofilho@754
   770
     */
renatofilho@754
   771
    io_status =
renatofilho@754
   772
        g_io_channel_write_chars(gmyth_socket->sd_io_ch, command->str,
renatofilho@754
   773
                                 command->len, &bytes_written, &error);
leo_sobral@1
   774
leo_sobral@1
   775
renatofilho@754
   776
    if ((io_status == G_IO_STATUS_ERROR) || (bytes_written <= 0)) {
renatofilho@754
   777
        gmyth_debug("[%s] Error while writing to socket", __FUNCTION__);
renatofilho@754
   778
        ret = FALSE;
renatofilho@754
   779
    } else if (bytes_written < command->len) {
renatofilho@754
   780
        gmyth_debug("[%s] Not all data was written socket", __FUNCTION__);
renatofilho@754
   781
        ret = FALSE;
renatofilho@754
   782
    }
leo_sobral@1
   783
renatofilho@754
   784
    io_status = g_io_channel_flush(gmyth_socket->sd_io_ch, &error);
leo_sobral@1
   785
renatofilho@754
   786
    if ((bytes_written != command->len)
renatofilho@754
   787
        || (io_status == G_IO_STATUS_ERROR)) {
renatofilho@754
   788
        gmyth_debug
renatofilho@754
   789
            ("[%s] Some problem occurred when sending data to the socket\n",
renatofilho@754
   790
             __FUNCTION__);
rosfran@698
   791
renatofilho@754
   792
        ret = TRUE;
renatofilho@754
   793
    }
leo_sobral@1
   794
renatofilho@754
   795
    g_mutex_unlock(gmyth_socket->mutex);
renatofilho@754
   796
  done:
renatofilho@754
   797
    if (error != NULL) {
renatofilho@754
   798
        gmyth_debug
renatofilho@754
   799
            ("[%s] Error found reading data from IO channel: (%d, %s)\n",
renatofilho@754
   800
             __FUNCTION__, error->code, error->message);
renatofilho@754
   801
        ret = FALSE;
renatofilho@754
   802
        g_error_free(error);
renatofilho@754
   803
    }
leo_sobral@1
   804
renatofilho@754
   805
    if (buffer != NULL)
renatofilho@754
   806
        g_free(buffer);
leo_sobral@1
   807
renatofilho@754
   808
    return ret;
leo_sobral@1
   809
}
leo_sobral@1
   810
leo_sobral@1
   811
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
leo_sobral@1
   812
 * supported by the backend and send the "ANN" command.
leo_sobral@1
   813
 * 
leo_sobral@1
   814
 * @param gmyth_socket the GMythSocket instance.
leo_sobral@1
   815
 * @param hostname_backend The backend hostname or IP address.
leo_sobral@1
   816
 * @param port The backend port to connect.
rosfran@177
   817
 * @param blocking_client A flag to choose between blocking and non-blocking
rosfran@177
   818
 * @param with_events	Sets the connection flag to receive events.
rosfran@177
   819
 * 										backend connection. 
leo_sobral@1
   820
 */
renatofilho@754
   821
static          gboolean
renatofilho@750
   822
gmyth_socket_connect_to_backend_and_events(GMythSocket * gmyth_socket,
renatofilho@754
   823
                                           const gchar * hostname_backend,
renatofilho@754
   824
                                           gint port,
renatofilho@754
   825
                                           gboolean blocking_client,
renatofilho@754
   826
                                           gboolean with_events)
leo_sobral@1
   827
{
renatofilho@754
   828
    if (!gmyth_socket_connect(gmyth_socket, hostname_backend, port)) {
renatofilho@754
   829
        gmyth_debug("[%s] Could not open socket to backend machine [%s]\n",
renatofilho@754
   830
                    __FUNCTION__, hostname_backend);
renatofilho@754
   831
        return FALSE;
renatofilho@754
   832
    }
leo_sobral@1
   833
renatofilho@754
   834
    if (gmyth_socket_check_protocol_version(gmyth_socket)) {
renatofilho@754
   835
        GString        *result;
renatofilho@754
   836
        GString        *base_str = g_string_new("");
renatofilho@754
   837
        GString        *hostname = NULL;
leo_sobral@1
   838
renatofilho@754
   839
        hostname = gmyth_socket_get_local_hostname();
renatofilho@754
   840
        if (hostname == NULL) {
renatofilho@754
   841
            gmyth_debug
renatofilho@754
   842
                ("Hostname not available, setting to n800frontend\n");
renatofilho@754
   843
            hostname = g_string_new("n800frontend");
renatofilho@754
   844
        }
rosfran@698
   845
renatofilho@754
   846
        g_string_printf(base_str, "ANN %s %s %u",
renatofilho@754
   847
                        (blocking_client ? "Playback" : "Monitor"),
renatofilho@754
   848
                        hostname->str, with_events);
leo_sobral@1
   849
renatofilho@754
   850
        gmyth_socket_send_command(gmyth_socket, base_str);
renatofilho@754
   851
        result = gmyth_socket_receive_response(gmyth_socket);
rosfran@698
   852
renatofilho@754
   853
        if (result != NULL) {
renatofilho@754
   854
            gmyth_debug("Response received from backend: %s", result->str);
renatofilho@754
   855
            g_string_free(result, TRUE);
renatofilho@754
   856
        }
rosfran@698
   857
renatofilho@754
   858
        g_string_free(hostname, TRUE);
renatofilho@754
   859
        g_string_free(base_str, TRUE);
rosfran@698
   860
renatofilho@754
   861
        return TRUE;
renatofilho@754
   862
    } else {
renatofilho@754
   863
        gmyth_debug("[%s] GMythSocket could not connect to the backend",
renatofilho@754
   864
                    __FUNCTION__);
renatofilho@754
   865
        return FALSE;
renatofilho@754
   866
    }
leo_sobral@1
   867
}
leo_sobral@1
   868
rosfran@177
   869
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
rosfran@177
   870
 * supported by the backend and send the "ANN" command.
rosfran@177
   871
 * 
rosfran@177
   872
 * @param gmyth_socket the GMythSocket instance.
rosfran@177
   873
 * @param hostname_backend The backend hostname or IP address.
rosfran@177
   874
 * @param port The backend port to connect.
rosfran@177
   875
 * @param blocking_client A flag to choose between blocking and non-blocking 
rosfran@177
   876
 */
rosfran@177
   877
gboolean
renatofilho@750
   878
gmyth_socket_connect_to_backend(GMythSocket * gmyth_socket,
renatofilho@754
   879
                                const gchar * hostname_backend, gint port,
renatofilho@754
   880
                                gboolean blocking_client)
rosfran@177
   881
{
renatofilho@754
   882
    if (!gmyth_socket_connect_to_backend_and_events
renatofilho@754
   883
        (gmyth_socket, hostname_backend, port, blocking_client, FALSE)) {
renatofilho@754
   884
        gmyth_debug("Could not open socket to backend machine [%s]\n",
renatofilho@754
   885
                    hostname_backend);
renatofilho@754
   886
        return FALSE;
renatofilho@754
   887
    }
rosfran@698
   888
renatofilho@754
   889
    return TRUE;
rosfran@177
   890
rosfran@177
   891
}
rosfran@177
   892
rosfran@177
   893
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
rosfran@177
   894
 * supported by the backend and send the "ANN" command.
rosfran@177
   895
 * 
rosfran@177
   896
 * @param gmyth_socket the GMythSocket instance.
rosfran@177
   897
 * @param hostname_backend The backend hostname or IP address.
rosfran@177
   898
 * @param port The backend port to connect.
rosfran@177
   899
 * @param blocking_client A flag to choose between blocking and non-blocking 
rosfran@177
   900
 */
rosfran@177
   901
gboolean
renatofilho@750
   902
gmyth_socket_connect_to_backend_events(GMythSocket * gmyth_socket,
renatofilho@754
   903
                                       const gchar * hostname_backend,
renatofilho@754
   904
                                       gint port, gboolean blocking_client)
rosfran@177
   905
{
renatofilho@754
   906
    if (!gmyth_socket_connect_to_backend_and_events
renatofilho@754
   907
        (gmyth_socket, hostname_backend, port, blocking_client, TRUE)) {
renatofilho@754
   908
        gmyth_debug
renatofilho@754
   909
            ("Could not open socket to backend machine in order to receive events [%s]\n",
renatofilho@754
   910
             hostname_backend);
renatofilho@754
   911
        return FALSE;
renatofilho@754
   912
    }
rosfran@698
   913
renatofilho@754
   914
    return TRUE;
rosfran@177
   915
}
rosfran@177
   916
leo_sobral@1
   917
/** Closes the socket connection to the backend.
leo_sobral@1
   918
 * 
leo_sobral@1
   919
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   920
 */
leo_sobral@1
   921
void
renatofilho@750
   922
gmyth_socket_close_connection(GMythSocket * gmyth_socket)
leo_sobral@1
   923
{
renatofilho@754
   924
    /*
renatofilho@754
   925
     * if ( gmyth_socket->sd != -1 ) { close (gmyth_socket->sd);
renatofilho@754
   926
     * gmyth_socket->sd = -1; } 
renatofilho@754
   927
     */
renatofilho@754
   928
renatofilho@754
   929
    if (gmyth_socket->sd_io_ch != NULL) {
renatofilho@754
   930
        g_io_channel_shutdown(gmyth_socket->sd_io_ch, TRUE, NULL);
renatofilho@754
   931
        g_io_channel_unref(gmyth_socket->sd_io_ch);
renatofilho@754
   932
        gmyth_socket->sd_io_ch = NULL;
renatofilho@754
   933
        gmyth_socket->sd = -1;
renatofilho@750
   934
    }
rosfran@698
   935
leo_sobral@1
   936
}
leo_sobral@1
   937
leo_sobral@1
   938
leo_sobral@1
   939
/** Try the MythTV version numbers, and get the version returned by
leo_sobral@1
   940
 * the possible REJECT message, in order to contruct a new
leo_sobral@1
   941
 * MythTV version request.
leo_sobral@1
   942
 * 
leo_sobral@1
   943
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
   944
 * @param mythtv_version The Mythtv protocol version to be tested
rosfran@101
   945
 * 
rosfran@101
   946
 * @return The actual MythTV the client is connected to.
leo_sobral@1
   947
 */
rosfran@101
   948
gint
renatofilho@750
   949
gmyth_socket_check_protocol_version_number(GMythSocket * gmyth_socket,
renatofilho@754
   950
                                           gint mythtv_version)
leo_sobral@1
   951
{
renatofilho@754
   952
    GString        *response = NULL;
renatofilho@754
   953
    GString        *payload = NULL;
renatofilho@754
   954
    gboolean        res = TRUE;
renatofilho@754
   955
    gint            mythtv_new_version = MYTHTV_CANNOT_NEGOTIATE_VERSION;
renatofilho@754
   956
    guint           max_iterations = MYTHTV_MAX_VERSION_CHECKS;
leo_sobral@1
   957
renatofilho@754
   958
    assert(gmyth_socket);
leo_sobral@446
   959
renatofilho@754
   960
  try_new_version:
renatofilho@754
   961
    payload = g_string_new("MYTH_PROTO_VERSION");
renatofilho@754
   962
    g_string_append_printf(payload, " %d", mythtv_version);
leo_sobral@1
   963
renatofilho@754
   964
    gmyth_socket_send_command(gmyth_socket, payload);
renatofilho@754
   965
    response = gmyth_socket_receive_response(gmyth_socket);
leo_sobral@1
   966
renatofilho@754
   967
    if (response == NULL) {
renatofilho@754
   968
        gmyth_debug("[%s] Check protocol version error! Not answered!",
renatofilho@754
   969
                    __FUNCTION__);
renatofilho@754
   970
        res = FALSE;
renatofilho@754
   971
        goto done;
renatofilho@754
   972
    }
leo_sobral@1
   973
renatofilho@754
   974
    res = g_str_has_prefix(response->str, "ACCEPT");
renatofilho@754
   975
    if (!res) {
renatofilho@754
   976
        gmyth_debug("[%s] Protocol version request error: %s",
renatofilho@754
   977
                    __FUNCTION__, response->str);
renatofilho@754
   978
        /*
renatofilho@754
   979
         * get the version number returned by the REJECT message 
renatofilho@754
   980
         */
renatofilho@754
   981
        if ((res = g_str_has_prefix(response->str, "REJECT")) == TRUE) {
renatofilho@754
   982
            gchar          *new_version = NULL;
rosfran@698
   983
renatofilho@754
   984
            new_version = g_strrstr(response->str, "]");
renatofilho@754
   985
            if (new_version != NULL) {
renatofilho@754
   986
                ++new_version;  /* skip ']' character */
renatofilho@754
   987
                if (new_version != NULL) {
renatofilho@754
   988
                    gmyth_debug("[%s] got MythTV version = %s.\n",
renatofilho@754
   989
                                __FUNCTION__, new_version);
renatofilho@754
   990
                    mythtv_version =
renatofilho@754
   991
                        (gint) g_ascii_strtoull(new_version, NULL, 10);
renatofilho@754
   992
                    /*
renatofilho@754
   993
                     * do reconnection to the socket (socket is closed if
renatofilho@754
   994
                     * the MythTV version was wrong) 
renatofilho@754
   995
                     */
renatofilho@754
   996
                    gmyth_socket_connect(gmyth_socket,
renatofilho@754
   997
                                         gmyth_socket->hostname,
renatofilho@754
   998
                                         gmyth_socket->port);
renatofilho@754
   999
                    new_version = NULL;
renatofilho@754
  1000
                    if (--max_iterations > 0) {
renatofilho@754
  1001
                        g_string_free(payload, TRUE);
renatofilho@754
  1002
                        g_string_free(response, TRUE);
renatofilho@754
  1003
                        goto try_new_version;
renatofilho@754
  1004
                    } else
renatofilho@754
  1005
                        goto done;
renatofilho@754
  1006
                }
renatofilho@754
  1007
            }
renatofilho@754
  1008
        }
renatofilho@754
  1009
    }
rosfran@698
  1010
renatofilho@754
  1011
    /*
renatofilho@754
  1012
     * change the return value to a valid one 
renatofilho@754
  1013
     */
renatofilho@754
  1014
    if (res) {
renatofilho@754
  1015
        mythtv_new_version = mythtv_version;
renatofilho@754
  1016
        gmyth_socket->mythtv_version = mythtv_new_version;
renatofilho@754
  1017
    }
leo_sobral@1
  1018
renatofilho@754
  1019
  done:
renatofilho@754
  1020
    g_string_free(payload, TRUE);
renatofilho@754
  1021
    g_string_free(response, TRUE);
leo_sobral@1
  1022
renatofilho@754
  1023
    return mythtv_new_version;
leo_sobral@1
  1024
}
leo_sobral@1
  1025
leo_sobral@1
  1026
/** Verifies if the Mythtv backend supported the GMyth supported version.
leo_sobral@1
  1027
 * 
leo_sobral@1
  1028
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1029
 * @return TRUE if supports, FALSE if not.
leo_sobral@1
  1030
 */
leo_sobral@1
  1031
gboolean
renatofilho@750
  1032
gmyth_socket_check_protocol_version(GMythSocket * gmyth_socket)
leo_sobral@1
  1033
{
renatofilho@754
  1034
    return ((gmyth_socket->mythtv_version =
renatofilho@754
  1035
             gmyth_socket_check_protocol_version_number(gmyth_socket,
renatofilho@754
  1036
                                                        MYTHTV_VERSION_DEFAULT))
renatofilho@754
  1037
            != MYTHTV_CANNOT_NEGOTIATE_VERSION);
rosfran@101
  1038
}
rosfran@101
  1039
rosfran@101
  1040
/** Returns the Mythtv backend supported version.
rosfran@101
  1041
 * 
rosfran@101
  1042
 * @param gmyth_socket The GMythSocket instance.
rosfran@101
  1043
 * @return The actual MythTV version number.
rosfran@101
  1044
 */
rosfran@101
  1045
gint
renatofilho@750
  1046
gmyth_socket_get_protocol_version(GMythSocket * gmyth_socket)
rosfran@101
  1047
{
renatofilho@754
  1048
    return gmyth_socket->mythtv_version;
leo_sobral@1
  1049
}
leo_sobral@1
  1050
leo_sobral@1
  1051
/** Receives a backend answer after a gmyth_socket_send_command_call ().
leo_sobral@1
  1052
 * 
leo_sobral@1
  1053
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1054
 * @return The response received, or NULL if error or nothing was received.
leo_sobral@1
  1055
 */
renatofilho@754
  1056
GString        *
renatofilho@750
  1057
gmyth_socket_receive_response(GMythSocket * gmyth_socket)
leo_sobral@1
  1058
{
renatofilho@754
  1059
    GIOStatus       io_status = G_IO_STATUS_NORMAL;
renatofilho@754
  1060
    GError         *error = NULL;
renatofilho@754
  1061
    gchar          *buffer = NULL;
leo_sobral@1
  1062
renatofilho@754
  1063
    GString        *str = NULL;
leo_sobral@1
  1064
renatofilho@754
  1065
    gsize           bytes_read = 0;
renatofilho@754
  1066
    gint            len = 0;
rosfran@698
  1067
renatofilho@754
  1068
    if (gmyth_socket == NULL)
renatofilho@754
  1069
        return NULL;
rosfran@336
  1070
renatofilho@754
  1071
    GIOCondition    io_cond;
leo_sobral@1
  1072
renatofilho@754
  1073
    /*
renatofilho@754
  1074
     * verify if the input (read) buffer is ready to receive data 
renatofilho@754
  1075
     */
renatofilho@754
  1076
    g_mutex_lock(gmyth_socket->mutex);
rosfran@698
  1077
renatofilho@754
  1078
    buffer = g_strnfill(MYTH_PROTOCOL_FIELD_SIZE, ' ');
renatofilho@754
  1079
    if (NULL == gmyth_socket->sd_io_ch) {
renatofilho@754
  1080
        gmyth_socket_connect(gmyth_socket, gmyth_socket->hostname,
renatofilho@754
  1081
                             gmyth_socket->port);
renatofilho@754
  1082
    }
rosfran@698
  1083
renatofilho@754
  1084
    io_cond = g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
renatofilho@754
  1085
    /*
renatofilho@754
  1086
     * if ( NULL == gmyth_socket->sd_io_ch->read_buf || ( NULL ==
renatofilho@754
  1087
     * gmyth_socket->sd_io_ch->read_buf->str ) ) gmyth_socket->sd_io_ch =
renatofilho@754
  1088
     * g_io_channel_unix_new( gmyth_socket->sd ); 
renatofilho@754
  1089
     */
rosfran@698
  1090
renatofilho@754
  1091
    if (gmyth_socket->sd_io_ch->is_readable /* && !( ( io_cond & G_IO_IN ) 
renatofilho@754
  1092
                                             * == 0 ) */ )
renatofilho@754
  1093
        io_status =
renatofilho@754
  1094
            g_io_channel_read_chars(gmyth_socket->sd_io_ch, buffer,
renatofilho@754
  1095
                                    MYTH_PROTOCOL_FIELD_SIZE, &bytes_read,
renatofilho@754
  1096
                                    &error);
renatofilho@754
  1097
    else
renatofilho@754
  1098
        return g_string_new("");
leo_sobral@1
  1099
renatofilho@754
  1100
    /*
renatofilho@754
  1101
     * verify if the input (read) buffer is ready to receive data 
renatofilho@754
  1102
     */
renatofilho@754
  1103
    io_cond = g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
rosfran@698
  1104
renatofilho@754
  1105
    // if ( ( io_cond & G_IO_IN ) == 0 ) 
renatofilho@754
  1106
    // return NULL; 
leo_sobral@1
  1107
renatofilho@754
  1108
    gmyth_debug("[%s] Bytes read = %d\n", __FUNCTION__, bytes_read);
leo_sobral@1
  1109
renatofilho@754
  1110
    if ((io_status == G_IO_STATUS_ERROR) || (bytes_read <= 0)) {
renatofilho@754
  1111
        gmyth_debug("[%s] Error in mythprotocol response from backend\n",
renatofilho@754
  1112
                    __FUNCTION__);
renatofilho@754
  1113
        str = NULL;
renatofilho@754
  1114
        // return NULL;
renatofilho@754
  1115
    } else if (buffer != NULL && strlen(buffer) > 0) {
leo_sobral@1
  1116
renatofilho@754
  1117
        // io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error
renatofilho@754
  1118
        // );
renatofilho@754
  1119
        /*
renatofilho@754
  1120
         * verify if the input (read) buffer is ready to receive data 
renatofilho@754
  1121
         */
renatofilho@754
  1122
        // io_cond = g_io_channel_get_buffer_condition(
renatofilho@754
  1123
        // gmyth_socket->sd_io_ch );
rosfran@698
  1124
renatofilho@754
  1125
        // if ( ( io_cond & G_IO_IN ) != 0 ) {
renatofilho@754
  1126
        // gchar *buffer_aux = NULL;
renatofilho@147
  1127
renatofilho@754
  1128
        /*
renatofilho@754
  1129
         * removes trailing whitespace 
renatofilho@754
  1130
         */
renatofilho@754
  1131
        // buffer_aux = g_strstrip (buffer);
renatofilho@754
  1132
        len = (gint) g_ascii_strtoull(g_strstrip(buffer), NULL, 10);
renatofilho@147
  1133
renatofilho@754
  1134
        g_free(buffer);
rosfran@698
  1135
renatofilho@754
  1136
        /*
renatofilho@754
  1137
         * if (buffer_aux != NULL) { g_free (buffer_aux); buffer_aux =
renatofilho@754
  1138
         * NULL; } 
renatofilho@754
  1139
         */
renatofilho@147
  1140
renatofilho@754
  1141
        buffer = g_new0(gchar, len + 1);
rosfran@698
  1142
renatofilho@754
  1143
        bytes_read = 0;
renatofilho@754
  1144
        if (!(gmyth_socket != NULL && gmyth_socket->sd_io_ch != NULL))
renatofilho@754
  1145
            return NULL;
melunko@412
  1146
renatofilho@754
  1147
        if (gmyth_socket->sd_io_ch->is_readable)
renatofilho@754
  1148
            io_status =
renatofilho@754
  1149
                g_io_channel_read_chars(gmyth_socket->sd_io_ch, buffer,
renatofilho@754
  1150
                                        len, &bytes_read, &error);
renatofilho@754
  1151
        else
renatofilho@754
  1152
            return g_string_new("");
rosfran@698
  1153
renatofilho@754
  1154
        buffer[bytes_read] = '\0';
renatofilho@754
  1155
        // }
renatofilho@754
  1156
    }
leo_sobral@1
  1157
renatofilho@754
  1158
    g_mutex_unlock(gmyth_socket->mutex);
renatofilho@754
  1159
    // g_static_rw_lock_reader_unlock (&rwlock);
leo_sobral@1
  1160
renatofilho@754
  1161
    gmyth_debug("Response received from backend: ----- {%s}\n", buffer);
renatofilho@754
  1162
    if ((bytes_read != len) || (io_status == G_IO_STATUS_ERROR))
renatofilho@754
  1163
        str = NULL;
renatofilho@754
  1164
    else
renatofilho@754
  1165
        str = g_string_new(buffer);
leo_sobral@1
  1166
renatofilho@754
  1167
    if (error != NULL) {
renatofilho@754
  1168
        gmyth_debug
renatofilho@754
  1169
            ("[%s] Error found receiving response from the IO channel: (%d, %s)\n",
renatofilho@754
  1170
             __FUNCTION__, error->code, error->message);
renatofilho@754
  1171
        str = NULL;
renatofilho@754
  1172
        g_error_free(error);
renatofilho@754
  1173
    }
leo_sobral@1
  1174
renatofilho@754
  1175
    g_free(buffer);
renatofilho@754
  1176
    return str;
leo_sobral@1
  1177
}
leo_sobral@1
  1178
leo_sobral@1
  1179
/** Format a Mythtv command from the str_list entries and send it to backend.
leo_sobral@1
  1180
 * 
leo_sobral@1
  1181
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1182
 * @param str_list The string list to form the command
leo_sobral@1
  1183
 * @return TRUE if command was sent, FALSE if any error happens.
leo_sobral@1
  1184
 */
leo_sobral@1
  1185
gboolean
renatofilho@750
  1186
gmyth_socket_write_stringlist(GMythSocket * gmyth_socket,
renatofilho@754
  1187
                              GMythStringList * str_list)
leo_sobral@1
  1188
{
leo_sobral@1
  1189
renatofilho@754
  1190
    GList          *tmp_list = NULL;
renatofilho@754
  1191
    GPtrArray      *ptr_array = NULL;
renatofilho@754
  1192
    gchar          *str_array = NULL;
leo_sobral@1
  1193
renatofilho@754
  1194
    g_mutex_lock(gmyth_socket->mutex);
renatofilho@754
  1195
    // g_static_rw_lock_writer_lock (&rwlock);
leo_sobral@1
  1196
renatofilho@754
  1197
    ptr_array = g_ptr_array_sized_new(g_list_length(str_list->glist));
leo_sobral@1
  1198
renatofilho@754
  1199
    // FIXME: change this implementation!
renatofilho@754
  1200
    tmp_list = str_list->glist;
renatofilho@754
  1201
    for (; tmp_list; tmp_list = tmp_list->next) {
renatofilho@754
  1202
        if (tmp_list->data != NULL) {
renatofilho@754
  1203
            g_ptr_array_add(ptr_array, ((GString *) tmp_list->data)->str);
renatofilho@754
  1204
        } else {
renatofilho@754
  1205
            g_ptr_array_add(ptr_array, "");
renatofilho@754
  1206
        }
renatofilho@754
  1207
    }
renatofilho@754
  1208
    g_ptr_array_add(ptr_array, NULL);   // g_str_joinv() needs a NULL
renatofilho@754
  1209
    // terminated string
leo_sobral@1
  1210
renatofilho@754
  1211
    str_array = g_strjoinv(MYTH_SEPARATOR, (gchar **) (ptr_array->pdata));
leo_sobral@1
  1212
renatofilho@754
  1213
    g_mutex_unlock(gmyth_socket->mutex);
renatofilho@754
  1214
    // g_static_rw_lock_writer_unlock (&rwlock);
leo_sobral@1
  1215
renatofilho@754
  1216
    gmyth_debug("[%s] Sending socket request: %s\n", __FUNCTION__,
renatofilho@754
  1217
                str_array);
rosfran@29
  1218
renatofilho@754
  1219
    // Sends message to backend 
renatofilho@754
  1220
    // TODO: implement looping to send remaining data, and add timeout
renatofilho@754
  1221
    // testing! 
renatofilho@754
  1222
    GString        *command = g_string_new(str_array);
rosfran@698
  1223
renatofilho@754
  1224
    gmyth_socket_send_command(gmyth_socket, command);
leo_sobral@446
  1225
renatofilho@754
  1226
    g_string_free(command, TRUE);
leo_sobral@1
  1227
renatofilho@754
  1228
    g_free(str_array);
leo_sobral@446
  1229
renatofilho@754
  1230
    /*
renatofilho@754
  1231
     * ptr_array is pointing to data inside str_list->glist 
renatofilho@754
  1232
     */
renatofilho@754
  1233
    g_ptr_array_free(ptr_array, TRUE);
leo_sobral@1
  1234
renatofilho@754
  1235
    return TRUE;
leo_sobral@1
  1236
}
leo_sobral@1
  1237
renatofilho@754
  1238
/*
renatofilho@754
  1239
 * Receives a backend command response and split it into the given string
renatofilho@754
  1240
 * list. @param gmyth_socket The GMythSocket instance. @param str_list
renatofilho@754
  1241
 * the string list to be filled. @return The number of received strings. 
leo_sobral@1
  1242
 */
leo_sobral@1
  1243
gint
renatofilho@750
  1244
gmyth_socket_read_stringlist(GMythSocket * gmyth_socket,
renatofilho@754
  1245
                             GMythStringList * str_list)
leo_sobral@1
  1246
{
renatofilho@754
  1247
    GString        *response;
renatofilho@754
  1248
    gint            i;
leo_sobral@1
  1249
renatofilho@754
  1250
    gmyth_string_list_clear_all(str_list);
leo_sobral@446
  1251
renatofilho@754
  1252
    response = gmyth_socket_receive_response(gmyth_socket);
renatofilho@754
  1253
    if (response != NULL && response->str != NULL && response->len > 0) {
renatofilho@754
  1254
        gchar         **str_array;
rosfran@698
  1255
renatofilho@754
  1256
        g_mutex_lock(gmyth_socket->mutex);
rosfran@698
  1257
renatofilho@754
  1258
        str_array = g_strsplit(response->str, MYTH_SEPARATOR, -1);
rosfran@698
  1259
renatofilho@754
  1260
        for (i = 0; i < g_strv_length(str_array); i++) {
renatofilho@754
  1261
            // if ( str_array[i] != NULL && strlen( str_array[i] ) > 0 )
renatofilho@754
  1262
            gmyth_string_list_append_char_array(str_list, str_array[i]);
renatofilho@754
  1263
        }
rosfran@698
  1264
renatofilho@754
  1265
        g_mutex_unlock(gmyth_socket->mutex);
renatofilho@754
  1266
        g_strfreev(str_array);
renatofilho@754
  1267
    }
leo_sobral@1
  1268
renatofilho@754
  1269
    g_string_free(response, TRUE);
leo_sobral@1
  1270
renatofilho@754
  1271
    return gmyth_string_list_length(str_list);
leo_sobral@1
  1272
}
leo_sobral@1
  1273
leo_sobral@1
  1274
/** Formats a Mythtv protocol command based on str_list and sends it to
leo_sobral@1
  1275
 * the connected backend. The backend response is overwritten into str_list.
leo_sobral@1
  1276
 *
leo_sobral@1
  1277
 * @param gmyth_socket The GMythSocket instance.
leo_sobral@1
  1278
 * @param str_list The string list to be sent, and on which the answer 
leo_sobral@1
  1279
 * will be written.
leo_sobral@1
  1280
 * @return TRUE if command was sent and an answer was received, FALSE if any
leo_sobral@1
  1281
 * error happens.
leo_sobral@1
  1282
 */
leo_sobral@1
  1283
gint
renatofilho@750
  1284
gmyth_socket_sendreceive_stringlist(GMythSocket * gmyth_socket,
renatofilho@754
  1285
                                    GMythStringList * str_list)
leo_sobral@1
  1286
{
renatofilho@754
  1287
    gmyth_socket_write_stringlist(gmyth_socket, str_list);
leo_sobral@1
  1288
renatofilho@754
  1289
    return gmyth_socket_read_stringlist(gmyth_socket, str_list);
leo_sobral@1
  1290
}