/** * 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); }