gmyth/src/gmyth_uri.c
author rosfran
Tue May 15 21:39:04 2007 +0100 (2007-05-15)
branchtrunk
changeset 655 ad1457d65f74
parent 643 653976fae5dd
child 661 a4fb2dd520ba
permissions -rwxr-xr-x
[svn r661] Fixed starvation loop when reading an unlimited-size buffer (LiveTV) from the FileTransfer; MonitorHandler is a GThread now.
     1 /** 
     2  * GMyth Library
     3  *
     4  * @file gmyth/gmyth_uri.c
     5  * 
     6  * @brief <p> GMythURI utils
     7  *  - Extracts and parses a URI char string, in according with the RFC 2396 
     8  *    [http://www.ietf.org/rfc/rfc2396.txt]
     9  * 
    10  * Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
    11  * @author Rosfran Borges <rosfran.borges@indt.org.br>
    12  *
    13  *//*
    14  * 
    15  * This program is free software; you can redistribute it and/or modify
    16  * it under the terms of the GNU Lesser General Public License as published by
    17  * the Free Software Foundation; either version 2 of the License, or
    18  * (at your option) any later version.
    19  *
    20  * This program is distributed in the hope that it will be useful,
    21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    23  * GNU General Public License for more details.
    24  *
    25  * You should have received a copy of the GNU Lesser General Public License
    26  * along with this program; if not, write to the Free Software
    27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    28  */
    29  
    30 #ifdef HAVE_CONFIG_H
    31 #include "config.h"
    32 #endif
    33 
    34 #include "gmyth_uri.h"
    35 #include "gmyth_socket.h"
    36 
    37 #include <glib.h>
    38 #include <string.h>
    39 #include <stdlib.h>
    40 
    41 #include "gmyth_debug.h"
    42 
    43 static void gmyth_uri_class_init          (GMythURIClass *klass);
    44 static void gmyth_uri_init                (GMythURI *object);
    45 
    46 static void gmyth_uri_dispose  (GObject *object);
    47 static void gmyth_uri_finalize (GObject *object);
    48 
    49 static void gmyth_uri_parser_setup_and_new(GMythURI *uri, const gchar *value);
    50 static gchar* gmyth_uri_print_field(const GString* field);
    51 
    52 G_DEFINE_TYPE(GMythURI, gmyth_uri, G_TYPE_OBJECT)
    53 
    54 static void
    55 gmyth_uri_class_init (GMythURIClass *klass)
    56 {
    57 	GObjectClass *gobject_class;
    58 
    59     gobject_class = (GObjectClass *) klass;
    60 	
    61     gobject_class->dispose  = gmyth_uri_dispose;
    62     gobject_class->finalize = gmyth_uri_finalize;	
    63 }
    64 
    65 static void
    66 gmyth_uri_init (GMythURI *gmyth_uri)
    67 {
    68 }
    69 
    70 static void
    71 gmyth_uri_dispose  (GObject *object)
    72 {
    73 	GMythURI *gmyth_uri = GMYTH_URI(object);
    74 
    75 	if ( gmyth_uri->host != NULL ) {
    76 		g_string_free( gmyth_uri->host, TRUE );
    77 		gmyth_uri->host = NULL;
    78 	}
    79 	
    80 	if ( gmyth_uri->protocol != NULL ) {
    81 		g_string_free( gmyth_uri->protocol, TRUE );
    82 		gmyth_uri->protocol = NULL;
    83 	}
    84 	
    85 	if ( gmyth_uri->path != NULL ) {
    86 		g_string_free( gmyth_uri->path, TRUE );
    87 		gmyth_uri->path = NULL;
    88 	}
    89 	
    90 	if ( gmyth_uri->fragment != NULL ) {
    91 		g_string_free( gmyth_uri->fragment, TRUE );
    92 		gmyth_uri->fragment = NULL;
    93 	}
    94 	
    95 	if ( gmyth_uri->user != NULL ) {
    96 		g_string_free( gmyth_uri->user, TRUE );
    97 		gmyth_uri->user = NULL;
    98 	}
    99 	
   100 	if ( gmyth_uri->password != NULL ) {
   101 		g_string_free( gmyth_uri->password, TRUE );
   102 		gmyth_uri->password = NULL;
   103 	}
   104 	
   105 	if ( gmyth_uri->query != NULL ) {
   106 		g_string_free( gmyth_uri->query, TRUE );
   107 		gmyth_uri->query = NULL;
   108 	}	
   109 
   110 	if ( gmyth_uri->uri != NULL ) {
   111 		g_string_free( gmyth_uri->uri, TRUE );
   112 		gmyth_uri->uri = NULL;
   113 	}	
   114 
   115 
   116 	G_OBJECT_CLASS (gmyth_uri_parent_class)->dispose (object);
   117 }
   118 
   119 static void
   120 gmyth_uri_finalize (GObject *object)
   121 {
   122 	//GMythURI *gmyth_uri = GMYTH_URI(object);
   123 
   124 	g_signal_handlers_destroy (object);
   125 
   126 	G_OBJECT_CLASS (gmyth_uri_parent_class)->finalize (object);
   127 }
   128 
   129 /** 
   130  * Creates a new instance of GMythURI.
   131  * 
   132  * @return a new instance of GMythURI.
   133  */
   134 GMythURI *
   135 gmyth_uri_new (void) 
   136 {
   137     GMythURI *gmyth_uri = GMYTH_URI (g_object_new (GMYTH_URI_TYPE, NULL));
   138     
   139     return gmyth_uri;
   140 }
   141 
   142 /** 
   143  * Creates a new instance of GMythURI.
   144  * 
   145  * @param uri_str The URI string representing this URI instance.
   146  * 
   147  * @return a new instance of GMythURI.
   148  */
   149 GMythURI *
   150 gmyth_uri_new_with_value (const gchar *uri_str) 
   151 {
   152     GMythURI *gmyth_uri = GMYTH_URI (g_object_new (GMYTH_URI_TYPE, NULL));
   153     
   154     gmyth_uri_parser_setup_and_new (gmyth_uri, uri_str);
   155     
   156     return gmyth_uri;
   157 }
   158 
   159 /** 
   160  * Gets the starting offset of a substring inside a given string.
   161  * 
   162  * @param haystack The given string to be searched for patterns.
   163  * @param needle The substring that should be matched over the haystack.
   164  * 
   165  * @return The starting offset to the given substring, or <code>-1</code> if the
   166  * 				 haystack function parameter doesn't contains the needle string argument.
   167  */
   168 static gint 
   169 gmyth_strstr (const gchar *haystack, const gchar *needle)
   170 {
   171 	
   172 	gchar *strPos;
   173 	
   174 	if (haystack == NULL || needle == NULL)
   175 		return -1;
   176 	strPos = strstr(haystack, needle);
   177 	if (strPos == NULL)
   178 		return -1;
   179 		
   180 	return (strPos - haystack);
   181 
   182 }
   183 
   184 /** 
   185  * Checks if a URI is absolute.
   186  * 
   187  * @param uri The GMythURI instance.
   188  * 
   189  * @return <code>true</code>, if the URI is absolute.
   190  */
   191 static gboolean
   192 gmyth_uri_isabsolute (const GMythURI *uri)
   193 {
   194 	gboolean ret = FALSE;
   195 	
   196 	g_return_val_if_fail( uri != NULL && uri->uri != NULL && uri->protocol != NULL, FALSE );
   197 	
   198 	if ( gmyth_strstr( uri->uri->str, GMYTH_URI_PROTOCOL_DELIM ) == 0 || strlen(uri->protocol->str) > 0 )
   199 		ret = TRUE;
   200 		
   201 	return ret;	
   202 }
   203 
   204 /** 
   205  * Searches for the first reverse character occurrence, from a given 
   206  * list of characters, inside a given string.
   207  * 
   208  * @param str The given string to be searched for characters occurrence.
   209  * @param chars The characters list. If this string returns 4 on strlen, there are
   210  * 						  four possible characters to be matched.
   211  * @param nchars The number of characters to be matched, which has at most 
   212  * 							 strlen(chars).
   213  * 
   214  * @return The starting offset to the first character occurrence, 
   215  *         or <code>-1</code> if the no character of the list could be found.
   216  */
   217 static gint
   218 gmyth_strrchr( const gchar *str, const gchar *chars, const gint nchars )
   219 {
   220 
   221 	gint strLen;
   222 	gint i, j;
   223 	
   224 	if ( str == NULL || chars == NULL )
   225 		return -1;
   226 		
   227 	strLen = strlen( str );
   228 	for ( i= (strLen-1); 0 <= i; i-- ) {
   229 		for ( j=0; j<nchars; j++ ) {
   230 			if ( str[i] == chars[j] )
   231 				return i;
   232 		}
   233 	}
   234 
   235 	return -1;
   236 
   237 }
   238 
   239 static gchar*
   240 gmyth_uri_print_field( const GString* field )
   241 {
   242 	if ( field != NULL && field->str != NULL && strlen(field->str) > 0 )
   243 		return field->str;
   244 	else
   245 		return "";		
   246 }
   247 
   248 /** 
   249  * Parses a URI string into a GMythURI instance.
   250  * 
   251  * @param uri The GMythURI instance.
   252  * @param value The URI string to be parsed.
   253  *
   254  */
   255 static void
   256 gmyth_uri_parser_setup_and_new( GMythURI *uri, const gchar *value )
   257 {
   258 	
   259 	gint 		uriLen;
   260 	gint 		currIdx;
   261 	gint 		protoIdx;
   262 	gint 		atIdx;
   263 	gint 		colonIdx;
   264 	gint 		shashIdx;
   265 	gint 		eIdx;
   266 	gchar       *host;
   267 	gint 		eblacketIdx;
   268 	gint 		hostLen;
   269 	gint 		sharpIdx;
   270 	/*
   271 	gint 		questionIdx;
   272 	gint 		queryLen;
   273 	*/
   274 	
   275 	uriLen = strlen(value);
   276 	uri->uri = g_string_new( value );
   277 		
   278 	currIdx = 0;
   279 	
   280 	/*** Protocol ****/
   281 	protoIdx = gmyth_strstr (value, GMYTH_URI_PROTOCOL_DELIM);
   282 	if (0 < protoIdx) {
   283         uri->protocol = g_string_new_len (value, protoIdx);
   284 		currIdx += protoIdx + strlen( GMYTH_URI_PROTOCOL_DELIM );
   285 	}
   286 
   287 	/*** User (Password) ****/
   288 	atIdx = gmyth_strstr( value+currIdx, GMYTH_URI_USER_DELIM );
   289 	if ( 0 < atIdx ) {
   290 		colonIdx = gmyth_strstr( value+currIdx, GMYTH_URI_COLON_DELIM );
   291 
   292 		if (0 < colonIdx && colonIdx < atIdx) {
   293             uri->user = g_string_new_len (value+currIdx, colonIdx);
   294             uri->password = g_string_new_len (value+currIdx+colonIdx+1, atIdx - (colonIdx+1));
   295 		}
   296 		else
   297             uri->user = g_string_new_len (value+currIdx, atIdx - currIdx);
   298 		currIdx += atIdx + 1;
   299 	}
   300 
   301 	/*** Host (Port) ****/
   302 	shashIdx = gmyth_strstr( value+currIdx, GMYTH_URI_SLASH_DELIM );
   303 	if (0 < shashIdx)
   304         uri->host = g_string_new_len (value+currIdx, shashIdx);
   305 	else if ( gmyth_uri_isabsolute(uri) == TRUE )
   306         uri->host = g_string_new_len (value+currIdx, strlen (value) - currIdx);
   307 
   308 	host = gmyth_uri_get_host(uri);
   309 	colonIdx = gmyth_strrchr (host, GMYTH_URI_COLON_DELIM, 1);
   310 	eblacketIdx = gmyth_strrchr (host, GMYTH_URI_EBLACET_DELIM, 1);
   311 	if ( ( 0 < colonIdx ) && ( eblacketIdx < colonIdx ) ) {
   312         GString *portStr = NULL;
   313 		GString *hostStr = g_string_new  (host != NULL ? host : "");
   314 
   315 		hostLen = hostStr->len;
   316 		/**** host ****/
   317 		uri->host = g_string_erase (uri->host, 0, hostLen);
   318 		uri->host = g_string_insert_len (uri->host, 0, hostStr->str, colonIdx);
   319 		if (0 < hostLen) {
   320 			if (host[0] == '[' && host[hostLen-1] == ']')
   321                 uri->host = g_string_new_len (hostStr->str+1, colonIdx-2);
   322 		}
   323 		/**** port ****/
   324 		portStr = g_string_new_len (hostStr->str+colonIdx+1, hostLen-colonIdx-1);
   325 		uri->port = (gint)g_ascii_strtoull( portStr->str, NULL, 10 );
   326 		g_string_free (portStr, TRUE);
   327 		g_string_free (hostStr, TRUE);
   328 	}
   329 	else {
   330         const gchar* protocol = gmyth_uri_get_protocol(uri);
   331 		uri->port = GMYTH_URI_KNKOWN_PORT;
   332 		if ( strcmp(protocol, GMYTH_URI_PROTOCOL_HTTP) == 0 )
   333 			uri->port = GMYTH_URI_DEFAULT_HTTP_PORT;
   334 		if ( strcmp(protocol, GMYTH_URI_PROTOCOL_FTP) == 0 )
   335 			uri->port = GMYTH_URI_DEFAULT_FTP_PORT;
   336 	}
   337 	
   338 	if (shashIdx > 0) currIdx += shashIdx;
   339 	
   340 	/*
   341 		Handle relative URL
   342 	*/
   343 	if (gmyth_uri_isabsolute(uri) == FALSE)
   344 	{
   345 
   346 		if (shashIdx != 0)
   347 		{
   348 			/* Add slash delimiter at the beginning of the URL,
   349 			   if it doesn't exist 
   350 			*/
   351 			uri->path = g_string_new( GMYTH_URI_SLASH_DELIM );
   352 		}
   353 		uri->path = g_string_append( uri->path, value );
   354 		
   355 	} else {
   356 		/* First set path simply to the rest of URI */
   357 		uri->path = g_string_new_len (value+currIdx,  uriLen-currIdx );
   358 	}
   359 	
   360 	//gmyth_debug( "uri value:  %s", value );
   361 	uri->query = g_string_new ( g_strstr_len( value, strlen(value), GMYTH_URI_QUESTION_DELIM ) );
   362 	
   363 	eIdx = gmyth_strstr( value+currIdx, GMYTH_URI_QUESTION_DELIM );
   364 	
   365 	if ( 0 < eIdx ) {
   366 		uri->query = g_string_new ( g_strstr_len( value, strlen(value), GMYTH_URI_QUESTION_DELIM ) );
   367 		gmyth_debug( "query = %s", uri->query->str );
   368 	}
   369 	
   370 	/**** Path (Query/Fragment) ****/
   371 	sharpIdx = gmyth_strstr(value+currIdx, GMYTH_URI_E_DELIM);
   372 	if (0 < sharpIdx) {
   373 		uri->path = g_string_append_len( uri->path, value+currIdx, sharpIdx);
   374 		uri->fragment = g_string_new_len (value+currIdx+sharpIdx+1, uriLen-(currIdx+sharpIdx+1));
   375 	}	
   376 
   377 	gmyth_debug( "[%s] GMythURI: host = %s, port = %d, path = %s, query = %s, fragment = %s, "\
   378       			 "user = %s, password = %s.\n", __FUNCTION__, 
   379                  gmyth_uri_print_field( uri->host ), uri->port,
   380 			     gmyth_uri_print_field( uri->path ), 
   381                  gmyth_uri_print_field( uri->query ), 
   382                  gmyth_uri_print_field( uri->fragment ), 
   383                  gmyth_uri_print_field ( uri->user ), 
   384                  gmyth_uri_print_field( uri->password ) );
   385 
   386 }
   387 
   388 /** 
   389  * Compares 2 URI instances, and checks them for equality.
   390  * 
   391  * @param uri The first GMythURI instance for comparison.
   392  * @param uri The second GMythURI instance for comparison.
   393  * 
   394  * @return <code>true</code>, if these two URI instances are equals.
   395  */
   396 gboolean
   397 gmyth_uri_is_equals( GMythURI* uri1, GMythURI* uri2 )
   398 {
   399 	return ( g_ascii_strcasecmp( gmyth_uri_get_host( uri1 ), gmyth_uri_get_host( uri2 ) ) == 0 &&
   400 				gmyth_uri_get_port( uri1 ) == gmyth_uri_get_port( uri2 ) );
   401 }
   402 
   403 /** 
   404  * Checks if the URI instance represents a LiveTV recording.
   405  * 
   406  * @param uri The GMythURI instance.
   407  * 
   408  * @return <code>true</code>, if the URI points to LiveTV content.
   409  */
   410 gboolean
   411 gmyth_uri_is_livetv( GMythURI* uri )
   412 {
   413 	gboolean ret = FALSE;
   414     
   415 	g_return_val_if_fail (uri != NULL, FALSE);
   416 	g_return_val_if_fail (uri->uri != NULL, FALSE);
   417     g_return_val_if_fail (uri->uri->str != NULL, FALSE);
   418    
   419     g_debug ("verify [%s]", uri->uri->str);
   420     
   421     if ((strstr (uri->uri->str, "channel") == NULL) ||
   422         (strstr (uri->uri->str, "livetv") == NULL)) 
   423         ret = FALSE;
   424 
   425 	if (ret)
   426 		gmyth_debug( "This URI is a LiveTV recording..." );
   427     else
   428         gmyth_debug( "This URI is a stored remote recording." );
   429 
   430 	return ret;
   431 	
   432 }
   433 
   434 /** 
   435  * Gets the channel name fro a URI instance.
   436  * 
   437  * @param uri The GMythURI instance.
   438  * 
   439  * @return The channel name, got from the substring "?channel=[channel_name]"
   440  * 				 of the URI string.
   441  */
   442 gchar*
   443 gmyth_uri_get_channel_name( GMythURI* uri )
   444 {
   445 	gchar* channel = NULL;
   446 	
   447 	g_return_val_if_fail( uri != NULL && uri->uri != NULL && uri->uri->str != NULL, FALSE );
   448 	
   449 	gchar *channel_query = g_strstr_len( gmyth_uri_get_query( uri ), strlen( gmyth_uri_get_query( uri ) ), "channel" );
   450 	
   451 	if ( channel_query != NULL )
   452 	{
   453 		gchar **chan_key_value = g_strsplit( gmyth_uri_get_query( uri ), "=", 2 );
   454 		
   455 		/* gmyth_debug( "Channel tuple is [ %s, %s ]", chan_key_value[0], chan_key_value[1] ); */
   456 
   457 		if ( chan_key_value[1] != NULL && strlen( chan_key_value[1] ) > 0 )
   458 		{
   459 			channel = g_strdup( chan_key_value[1] ); 
   460 		}
   461 
   462 		if ( chan_key_value != NULL )		
   463 			g_strfreev( chan_key_value );
   464 	}
   465 	
   466 	gmyth_debug( "Got channel decimal value from the URI: %s", channel );
   467 
   468 	return channel;
   469 	
   470 }
   471 
   472 /** 
   473  * Gets the channel number from a URI instance.
   474  * 
   475  * @param uri The GMythURI instance.
   476  * 
   477  * @return The channel number, got from the substring "?channel=[channel_number]"
   478  * 				 of the URI string, or <code>-1</code> it if couldn't be converted.
   479  */
   480 gint
   481 gmyth_uri_get_channel_num( GMythURI* uri )
   482 {
   483 	gchar *channel_name = gmyth_uri_get_channel_name( uri );
   484 	
   485 	if ( channel_name != NULL )
   486 	{
   487 		return g_ascii_strtoull( channel_name, NULL, 10 ); 
   488 	}
   489 	
   490 	return -1;
   491 	
   492 }
   493 
   494 /** 
   495  * Checks if the URI instance represents a reference to a local file.
   496  * 
   497  * @param uri The GMythURI instance.
   498  * 
   499  * @return <code>true</code>, if the URI points to a local file.
   500  */
   501 gboolean
   502 gmyth_uri_is_local_file( const GMythURI* uri )
   503 {
   504     gboolean ret = FALSE;
   505     gint len = -1;
   506     
   507     GString *hostname = gmyth_socket_get_local_hostname();
   508     
   509     g_return_val_if_fail( uri != NULL, FALSE );    
   510     
   511     len = strlen( gmyth_uri_get_host(uri) );
   512     
   513    // gmyth_debug("URI: host = %s, hostname = %s.", uri->host->str, hostname != NULL ? hostname->str : "[no hostname]");
   514     
   515     ret = ( NULL != hostname && ( g_ascii_strncasecmp( uri->host->str, 
   516               (hostname)->str, len ) == 0 ) /*|| 
   517         ( g_ascii_strncasecmp( gmyth_uri_get_host(uri), gmyth_socket_get_primary_addr(), len ) == 0 ) */ );
   518     
   519     if ( ret )
   520         gmyth_debug( "This URI is a local file..." );
   521     else
   522         gmyth_debug( "This URI is NOT a local file..." );
   523     
   524     return ret;
   525     
   526 }
   527 
   528 char*
   529 gmyth_uri_to_string (const GMythURI* uri)
   530 {
   531     g_return_val_if_fail (uri != NULL, NULL);
   532     g_return_val_if_fail (uri->uri != NULL, NULL);
   533     
   534     return g_strdup (uri->uri->str);	
   535 }