/**
* GMyth Library
*
* @file gmyth/gmyth_uri.c
*
* @brief
GMythURI utils
* - Extracts and parses a URI char string, in according with the RFC 2396
* [http://www.ietf.org/rfc/rfc2396.txt]
*
* Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
* @author Rosfran Borges
*
*//*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gmyth_uri.h"
#include "gmyth_socket.h"
#include
#include
#include
#include "gmyth_debug.h"
static void gmyth_uri_class_init (GMythURIClass *klass);
static void gmyth_uri_init (GMythURI *object);
static void gmyth_uri_dispose (GObject *object);
static void gmyth_uri_finalize (GObject *object);
static void gmyth_uri_parser_setup_and_new(GMythURI *uri, const gchar *value);
static gchar* gmyth_uri_print_field(const GString* field);
G_DEFINE_TYPE(GMythURI, gmyth_uri, G_TYPE_OBJECT)
static void
gmyth_uri_class_init (GMythURIClass *klass)
{
GObjectClass *gobject_class;
gobject_class = (GObjectClass *) klass;
gobject_class->dispose = gmyth_uri_dispose;
gobject_class->finalize = gmyth_uri_finalize;
}
static void
gmyth_uri_init (GMythURI *gmyth_uri)
{
}
static void
gmyth_uri_dispose (GObject *object)
{
GMythURI *gmyth_uri = GMYTH_URI(object);
if ( gmyth_uri->host != NULL ) {
g_string_free( gmyth_uri->host, TRUE );
gmyth_uri->host = NULL;
}
if ( gmyth_uri->protocol != NULL ) {
g_string_free( gmyth_uri->protocol, TRUE );
gmyth_uri->protocol = NULL;
}
if ( gmyth_uri->path != NULL ) {
g_string_free( gmyth_uri->path, TRUE );
gmyth_uri->path = NULL;
}
if ( gmyth_uri->fragment != NULL ) {
g_string_free( gmyth_uri->fragment, TRUE );
gmyth_uri->fragment = NULL;
}
if ( gmyth_uri->user != NULL ) {
g_string_free( gmyth_uri->user, TRUE );
gmyth_uri->user = NULL;
}
if ( gmyth_uri->password != NULL ) {
g_string_free( gmyth_uri->password, TRUE );
gmyth_uri->password = NULL;
}
if ( gmyth_uri->query != NULL ) {
g_string_free( gmyth_uri->query, TRUE );
gmyth_uri->query = NULL;
}
if ( gmyth_uri->uri != NULL ) {
g_string_free( gmyth_uri->uri, TRUE );
gmyth_uri->uri = NULL;
}
G_OBJECT_CLASS (gmyth_uri_parent_class)->dispose (object);
}
static void
gmyth_uri_finalize (GObject *object)
{
//GMythURI *gmyth_uri = GMYTH_URI(object);
g_signal_handlers_destroy (object);
G_OBJECT_CLASS (gmyth_uri_parent_class)->finalize (object);
}
/**
* Creates a new instance of GMythURI.
*
* @return a new instance of GMythURI.
*/
GMythURI *
gmyth_uri_new (void)
{
GMythURI *gmyth_uri = GMYTH_URI (g_object_new (GMYTH_URI_TYPE, NULL));
return gmyth_uri;
}
/**
* Creates a new instance of GMythURI.
*
* @param uri_str The URI string representing this URI instance.
*
* @return a new instance of GMythURI.
*/
GMythURI *
gmyth_uri_new_with_value (const gchar *uri_str)
{
GMythURI *gmyth_uri = GMYTH_URI (g_object_new (GMYTH_URI_TYPE, NULL));
gmyth_uri_parser_setup_and_new (gmyth_uri, uri_str);
return gmyth_uri;
}
/**
* Gets the starting offset of a substring inside a given string.
*
* @param haystack The given string to be searched for patterns.
* @param needle The substring that should be matched over the haystack.
*
* @return The starting offset to the given substring, or -1
if the
* haystack function parameter doesn't contains the needle string argument.
*/
static gint
gmyth_strstr (const gchar *haystack, const gchar *needle)
{
gchar *strPos;
if (haystack == NULL || needle == NULL)
return -1;
strPos = strstr(haystack, needle);
if (strPos == NULL)
return -1;
return (strPos - haystack);
}
/**
* Checks if a URI is absolute.
*
* @param uri The GMythURI instance.
*
* @return true
, if the URI is absolute.
*/
static gboolean
gmyth_uri_isabsolute (const GMythURI *uri)
{
gboolean ret = FALSE;
g_return_val_if_fail( uri != NULL && uri->uri != NULL && uri->protocol != NULL, FALSE );
if ( gmyth_strstr( uri->uri->str, GMYTH_URI_PROTOCOL_DELIM ) == 0 || strlen(uri->protocol->str) > 0 )
ret = TRUE;
return ret;
}
/**
* Searches for the first reverse character occurrence, from a given
* list of characters, inside a given string.
*
* @param str The given string to be searched for characters occurrence.
* @param chars The characters list. If this string returns 4 on strlen, there are
* four possible characters to be matched.
* @param nchars The number of characters to be matched, which has at most
* strlen(chars).
*
* @return The starting offset to the first character occurrence,
* or -1
if the no character of the list could be found.
*/
static gint
gmyth_strrchr( const gchar *str, const gchar *chars, const gint nchars )
{
gint strLen;
gint i, j;
if ( str == NULL || chars == NULL )
return -1;
strLen = strlen( str );
for ( i= (strLen-1); 0 <= i; i-- ) {
for ( j=0; jstr != NULL && strlen(field->str) > 0 )
return field->str;
else
return "";
}
/**
* Parses a URI string into a GMythURI instance.
*
* @param uri The GMythURI instance.
* @param value The URI string to be parsed.
*
*/
static void
gmyth_uri_parser_setup_and_new( GMythURI *uri, const gchar *value )
{
gint uriLen;
gint currIdx;
gint protoIdx;
gint atIdx;
gint colonIdx;
gint shashIdx;
gint eIdx;
gchar *host;
gint eblacketIdx;
gint hostLen;
gint sharpIdx;
/*
gint questionIdx;
gint queryLen;
*/
uriLen = strlen(value);
uri->uri = g_string_new( value );
currIdx = 0;
/*** Protocol ****/
protoIdx = gmyth_strstr (value, GMYTH_URI_PROTOCOL_DELIM);
if (0 < protoIdx) {
uri->protocol = g_string_new_len (value, protoIdx);
currIdx += protoIdx + strlen( GMYTH_URI_PROTOCOL_DELIM );
}
/*** User (Password) ****/
atIdx = gmyth_strstr( value+currIdx, GMYTH_URI_USER_DELIM );
if ( 0 < atIdx ) {
colonIdx = gmyth_strstr( value+currIdx, GMYTH_URI_COLON_DELIM );
if (0 < colonIdx && colonIdx < atIdx) {
uri->user = g_string_new_len (value+currIdx, colonIdx);
uri->password = g_string_new_len (value+currIdx+colonIdx+1, atIdx - (colonIdx+1));
}
else
uri->user = g_string_new_len (value+currIdx, atIdx - currIdx);
currIdx += atIdx + 1;
}
/*** Host (Port) ****/
shashIdx = gmyth_strstr( value+currIdx, GMYTH_URI_SLASH_DELIM );
if (0 < shashIdx)
uri->host = g_string_new_len (value+currIdx, shashIdx);
else if ( gmyth_uri_isabsolute(uri) == TRUE )
uri->host = g_string_new_len (value+currIdx, strlen (value) - currIdx);
host = gmyth_uri_get_host(uri);
colonIdx = gmyth_strrchr (host, GMYTH_URI_COLON_DELIM, 1);
eblacketIdx = gmyth_strrchr (host, GMYTH_URI_EBLACET_DELIM, 1);
if ( ( 0 < colonIdx ) && ( eblacketIdx < colonIdx ) ) {
GString *portStr = NULL;
GString *hostStr = g_string_new (host != NULL ? host : "");
hostLen = hostStr->len;
/**** host ****/
uri->host = g_string_erase (uri->host, 0, hostLen);
uri->host = g_string_insert_len (uri->host, 0, hostStr->str, colonIdx);
if (0 < hostLen) {
if (host[0] == '[' && host[hostLen-1] == ']')
uri->host = g_string_new_len (hostStr->str+1, colonIdx-2);
}
/**** port ****/
portStr = g_string_new_len (hostStr->str+colonIdx+1, hostLen-colonIdx-1);
uri->port = (gint)g_ascii_strtoull( portStr->str, NULL, 10 );
g_string_free (portStr, TRUE);
g_string_free (hostStr, TRUE);
}
else {
const gchar* protocol = gmyth_uri_get_protocol(uri);
uri->port = GMYTH_URI_KNKOWN_PORT;
if ( strcmp(protocol, GMYTH_URI_PROTOCOL_HTTP) == 0 )
uri->port = GMYTH_URI_DEFAULT_HTTP_PORT;
if ( strcmp(protocol, GMYTH_URI_PROTOCOL_FTP) == 0 )
uri->port = GMYTH_URI_DEFAULT_FTP_PORT;
}
if (shashIdx > 0) currIdx += shashIdx;
/*
Handle relative URL
*/
if (gmyth_uri_isabsolute(uri) == FALSE)
{
if (shashIdx != 0)
{
/* Add slash delimiter at the beginning of the URL,
if it doesn't exist
*/
uri->path = g_string_new( GMYTH_URI_SLASH_DELIM );
}
uri->path = g_string_append( uri->path, value );
} else {
/* First set path simply to the rest of URI */
uri->path = g_string_new_len (value+currIdx, uriLen-currIdx );
}
//gmyth_debug( "uri value: %s", value );
uri->query = g_string_new ( g_strstr_len( value, strlen(value), GMYTH_URI_QUESTION_DELIM ) );
eIdx = gmyth_strstr( value+currIdx, GMYTH_URI_QUESTION_DELIM );
if ( 0 < eIdx ) {
uri->query = g_string_new ( g_strstr_len( value, strlen(value), GMYTH_URI_QUESTION_DELIM ) );
gmyth_debug( "query = %s", uri->query->str );
}
/**** Path (Query/Fragment) ****/
sharpIdx = gmyth_strstr(value+currIdx, GMYTH_URI_E_DELIM);
if (0 < sharpIdx) {
uri->path = g_string_append_len( uri->path, value+currIdx, sharpIdx);
uri->fragment = g_string_new_len (value+currIdx+sharpIdx+1, uriLen-(currIdx+sharpIdx+1));
}
gmyth_debug( "[%s] GMythURI: host = %s, port = %d, path = %s, query = %s, fragment = %s, "\
"user = %s, password = %s.\n", __FUNCTION__,
gmyth_uri_print_field( uri->host ), uri->port,
gmyth_uri_print_field( uri->path ),
gmyth_uri_print_field( uri->query ),
gmyth_uri_print_field( uri->fragment ),
gmyth_uri_print_field ( uri->user ),
gmyth_uri_print_field( uri->password ) );
}
/**
* Compares 2 URI instances, and checks them for equality.
*
* @param uri The first GMythURI instance for comparison.
* @param uri The second GMythURI instance for comparison.
*
* @return true
, if these two URI instances are equals.
*/
gboolean
gmyth_uri_is_equals( GMythURI* uri1, GMythURI* uri2 )
{
return ( g_ascii_strcasecmp( gmyth_uri_get_host( uri1 ), gmyth_uri_get_host( uri2 ) ) == 0 &&
gmyth_uri_get_port( uri1 ) == gmyth_uri_get_port( uri2 ) );
}
/**
* Checks if the URI instance represents a LiveTV recording.
*
* @param uri The GMythURI instance.
*
* @return true
, if the URI points to LiveTV content.
*/
gboolean
gmyth_uri_is_livetv( GMythURI* uri )
{
gboolean ret = TRUE;
g_return_val_if_fail (uri != NULL, FALSE);
g_return_val_if_fail (uri->uri != NULL, FALSE);
g_return_val_if_fail (uri->uri->str != NULL, FALSE);
if ((strstr (uri->uri->str, "channel") == NULL) ||
(strstr (uri->uri->str, "livetv") == NULL))
ret = FALSE;
if (ret)
gmyth_debug( "This URI is a LiveTV recording..." );
else
gmyth_debug( "This URI is a stored remote recording." );
return ret;
}
/**
* Gets the channel name fro a URI instance.
*
* @param uri The GMythURI instance.
*
* @return The channel name, got from the substring "?channel=[channel_name]"
* of the URI string.
*/
gchar*
gmyth_uri_get_channel_name( GMythURI* uri )
{
gchar* channel = NULL;
g_return_val_if_fail( uri != NULL && uri->uri != NULL && uri->uri->str != NULL, FALSE );
gchar *channel_query = g_strstr_len( gmyth_uri_get_query( uri ), strlen( gmyth_uri_get_query( uri ) ), "channel" );
if ( channel_query != NULL )
{
gchar **chan_key_value = g_strsplit( gmyth_uri_get_query( uri ), "=", 2 );
/* gmyth_debug( "Channel tuple is [ %s, %s ]", chan_key_value[0], chan_key_value[1] ); */
if ( chan_key_value[1] != NULL && strlen( chan_key_value[1] ) > 0 )
{
channel = g_strdup( chan_key_value[1] );
}
if ( chan_key_value != NULL )
g_strfreev( chan_key_value );
}
gmyth_debug( "Got channel decimal value from the URI: %s", channel );
return channel;
}
/**
* Gets the channel number from a URI instance.
*
* @param uri The GMythURI instance.
*
* @return The channel number, got from the substring "?channel=[channel_number]"
* of the URI string, or -1
it if couldn't be converted.
*/
gint
gmyth_uri_get_channel_num( GMythURI* uri )
{
gchar *channel_name = gmyth_uri_get_channel_name( uri );
if ( channel_name != NULL )
{
return g_ascii_strtoull( channel_name, NULL, 10 );
}
return -1;
}
/**
* Checks if the URI instance represents a reference to a local file.
*
* @param uri The GMythURI instance.
*
* @return true
, if the URI points to a local file.
*/
gboolean
gmyth_uri_is_local_file( const GMythURI* uri )
{
gboolean ret = FALSE;
gint len = -1;
GString *hostname = gmyth_socket_get_local_hostname();
g_return_val_if_fail( uri != NULL, FALSE );
len = strlen( gmyth_uri_get_host(uri) );
// gmyth_debug("URI: host = %s, hostname = %s.", uri->host->str, hostname != NULL ? hostname->str : "[no hostname]");
ret = ( NULL != hostname && ( g_ascii_strncasecmp( uri->host->str,
(hostname)->str, len ) == 0 ) /*||
( g_ascii_strncasecmp( gmyth_uri_get_host(uri), gmyth_socket_get_primary_addr(), len ) == 0 ) */ );
if ( ret )
gmyth_debug( "This URI is a local file..." );
else
gmyth_debug( "This URI is NOT a local file..." );
return ret;
}
char*
gmyth_uri_to_string (const GMythURI* uri)
{
g_return_val_if_fail (uri != NULL, NULL);
g_return_val_if_fail (uri->uri != NULL, NULL);
return g_strdup (uri->uri->str);
}