static char* StreamExtractorCreateMRL( char const* base, char const* subentry ) { struct vlc_memstream buffer; char* escaped; if( mrl_EscapeFragmentIdentifier( &escaped, subentry ) ) return NULL; if( vlc_memstream_open( &buffer ) ) { free( escaped ); return NULL; } vlc_memstream_puts( &buffer, base ); if( !strstr( base, "#" ) ) vlc_memstream_putc( &buffer, '#' ); vlc_memstream_printf( &buffer, "!/%s", escaped ); free( escaped ); return vlc_memstream_close( &buffer ) ? NULL : buffer.ptr; }
static void tt_node_ToText( struct vlc_memstream *p_stream, const tt_basenode_t *p_basenode, const tt_time_t *playbacktime ) { if( p_basenode->i_type == TT_NODE_TYPE_ELEMENT ) { const tt_node_t *p_node = (const tt_node_t *) p_basenode; if( tt_time_Valid( playbacktime ) && !tt_timings_Contains( &p_node->timings, playbacktime ) ) return; vlc_memstream_putc( p_stream, '<' ); vlc_memstream_puts( p_stream, p_node->psz_node_name ); tt_node_AttributesToText( p_stream, p_node ); if( tt_node_HasChild( p_node ) ) { vlc_memstream_putc( p_stream, '>' ); #ifdef TTML_DEMUX_DEBUG vlc_memstream_printf( p_stream, "<!-- starts %ld ends %ld -->", tt_time_Convert( &p_node->timings.begin ), tt_time_Convert( &p_node->timings.end ) ); #endif for( const tt_basenode_t *p_child = p_node->p_child; p_child; p_child = p_child->p_next ) { tt_node_ToText( p_stream, p_child, playbacktime ); } vlc_memstream_printf( p_stream, "</%s>", p_node->psz_node_name ); } else vlc_memstream_puts( p_stream, "/>" ); } else { const tt_textnode_t *p_textnode = (const tt_textnode_t *) p_basenode; vlc_memstream_puts( p_stream, p_textnode->psz_text ); } }
int OpenDemux( vlc_object_t* p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; ssize_t i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 2048 ); if( unlikely( i_peek <= 32 ) ) return VLC_EGENERIC; const char *psz_xml = (const char *) p_peek; size_t i_xml = i_peek; /* Try to probe without xml module/loading the full document */ char *psz_alloc = NULL; switch( GetQWBE(p_peek) ) { /* See RFC 3023 Part 4 */ case UINT64_C(0xFFFE3C003F007800): /* UTF16 BOM<? */ case UINT64_C(0xFFFE3C003F007400): /* UTF16 BOM<t */ case UINT64_C(0xFEFF003C003F0078): /* UTF16 BOM<? */ case UINT64_C(0xFEFF003C003F0074): /* UTF16 BOM<t */ psz_alloc = FromCharset( "UTF-16", p_peek, i_peek ); break; case UINT64_C(0x3C003F0078006D00): /* UTF16-LE <?xm */ case UINT64_C(0x3C003F0074007400): /* UTF16-LE <tt */ psz_alloc = FromCharset( "UTF-16LE", p_peek, i_peek ); break; case UINT64_C(0x003C003F0078006D): /* UTF16-BE <?xm */ case UINT64_C(0x003C003F00740074): /* UTF16-BE <tt */ psz_alloc = FromCharset( "UTF-16BE", p_peek, i_peek ); break; case UINT64_C(0xEFBBBF3C3F786D6C): /* UTF8 BOM<?xml */ case UINT64_C(0x3C3F786D6C207665): /* UTF8 <?xml ve */ case UINT64_C(0xEFBBBF3C74742078): /* UTF8 BOM<tt x*/ break; default: if(GetDWBE(p_peek) != UINT32_C(0x3C747420)) /* tt node without xml document marker */ return VLC_EGENERIC; } if( psz_alloc ) { psz_xml = psz_alloc; i_xml = strlen( psz_alloc ); } /* Simplified probing. Valid TTML must have a namespace declaration */ const char *psz_tt = strnstr( psz_xml, "tt ", i_xml ); if( !psz_tt || psz_tt == psz_xml || (psz_tt[-1] != ':' && psz_tt[-1] != '<') ) { free( psz_alloc ); return VLC_EGENERIC; } else { const char * const rgsz[] = { "=\"http://www.w3.org/ns/ttml\"", "=\"http://www.w3.org/2004/11/ttaf1\"", "=\"http://www.w3.org/2006/04/ttaf1\"", "=\"http://www.w3.org/2006/10/ttaf1\"", }; const char *psz_ns = NULL; for( size_t i=0; i<ARRAY_SIZE(rgsz) && !psz_ns; i++ ) { psz_ns = strnstr( psz_xml, rgsz[i], i_xml - (psz_tt - psz_xml) ); } free( psz_alloc ); if( !psz_ns ) return VLC_EGENERIC; } p_demux->p_sys = p_sys = calloc( 1, sizeof( *p_sys ) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; p_sys->b_first_time = true; p_sys->temporal_extent.i_type = TT_TIMINGS_PARALLEL; tt_time_Init( &p_sys->temporal_extent.begin ); tt_time_Init( &p_sys->temporal_extent.end ); tt_time_Init( &p_sys->temporal_extent.dur ); p_sys->temporal_extent.begin.base = 0; p_sys->p_xml = xml_Create( p_demux ); if( !p_sys->p_xml ) goto error; p_sys->p_reader = xml_ReaderCreate( p_sys->p_xml, p_demux->s ); if( !p_sys->p_reader ) goto error; #ifndef TTML_DEMUX_DEBUG p_sys->p_reader->obj.flags |= OBJECT_FLAGS_QUIET; #endif if( ReadTTML( p_demux ) != VLC_SUCCESS ) goto error; tt_timings_Resolve( (tt_basenode_t *) p_sys->p_rootnode, &p_sys->temporal_extent, &p_sys->times.p_array, &p_sys->times.i_count ); #ifdef TTML_DEMUX_DEBUG { struct vlc_memstream stream; if( vlc_memstream_open( &stream ) ) goto error; tt_time_t t; tt_time_Init( &t ); tt_node_ToText( &stream, (tt_basenode_t*)p_sys->p_rootnode, &t /* invalid */ ); vlc_memstream_putc( &stream, '\0' ); if( vlc_memstream_close( &stream ) == VLC_SUCCESS ) { msg_Dbg( p_demux, "%s", stream.ptr ); free( stream.ptr ); } } #endif p_demux->pf_demux = Demux; p_demux->pf_control = Control; es_format_t fmt; es_format_Init( &fmt, SPU_ES, VLC_CODEC_TTML ); p_sys->p_es = es_out_Add( p_demux->out, &fmt ); if( !p_sys->p_es ) goto error; es_format_Clean( &fmt ); return VLC_SUCCESS; error: CloseDemux( p_demux ); return VLC_EGENERIC; }
/***************************************************************************** * Connect: *****************************************************************************/ static int Connect( stream_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; vlc_url_t srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url; ssize_t val; /* Clean info */ free( p_sys->psz_location ); free( p_sys->psz_mime ); free( p_sys->psz_icy_genre ); free( p_sys->psz_icy_name ); free( p_sys->psz_icy_title ); vlc_http_auth_Init( &p_sys->auth ); vlc_http_auth_Init( &p_sys->proxy_auth ); p_sys->psz_location = NULL; p_sys->psz_mime = NULL; p_sys->i_icy_meta = 0; p_sys->i_icy_offset = 0; p_sys->psz_icy_name = NULL; p_sys->psz_icy_genre = NULL; p_sys->psz_icy_title = NULL; p_sys->b_has_size = false; p_sys->offset = 0; p_sys->size = 0; struct vlc_memstream stream; vlc_memstream_open(&stream); vlc_memstream_puts(&stream, "GET "); if( p_sys->b_proxy ) vlc_memstream_printf( &stream, "http://%s:%d", p_sys->url.psz_host, p_sys->url.i_port ); if( p_sys->url.psz_path == NULL || p_sys->url.psz_path[0] == '\0' ) vlc_memstream_putc( &stream, '/' ); else vlc_memstream_puts( &stream, p_sys->url.psz_path ); if( p_sys->url.psz_option != NULL ) vlc_memstream_printf( &stream, "?%s", p_sys->url.psz_option ); vlc_memstream_puts( &stream, " HTTP/1.0\r\n" ); vlc_memstream_printf( &stream, "Host: %s", p_sys->url.psz_host ); if( p_sys->url.i_port != 80 ) vlc_memstream_printf( &stream, ":%d", p_sys->url.i_port ); vlc_memstream_puts( &stream, "\r\n" ); /* User Agent */ vlc_memstream_printf( &stream, "User-Agent: %s\r\n", p_sys->psz_user_agent ); /* Referrer */ if (p_sys->psz_referrer) vlc_memstream_printf( &stream, "Referer: %s\r\n", p_sys->psz_referrer ); /* Authentication */ if( p_sys->url.psz_username != NULL && p_sys->url.psz_password != NULL ) { char *auth; auth = vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access), &p_sys->auth, "GET", p_sys->url.psz_path, p_sys->url.psz_username, p_sys->url.psz_password ); if( auth != NULL ) vlc_memstream_printf( &stream, "Authorization: %s\r\n", auth ); free( auth ); } /* Proxy Authentication */ if( p_sys->b_proxy && p_sys->proxy.psz_username != NULL && p_sys->proxy.psz_password != NULL ) { char *auth; auth = vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access), &p_sys->proxy_auth, "GET", p_sys->url.psz_path, p_sys->proxy.psz_username, p_sys->proxy.psz_password ); if( auth != NULL ) vlc_memstream_printf( &stream, "Proxy-Authorization: %s\r\n", auth ); free( auth ); } /* ICY meta data request */ vlc_memstream_puts( &stream, "Icy-MetaData: 1\r\n" ); vlc_memstream_puts( &stream, "\r\n" ); if( vlc_memstream_close( &stream ) ) return -1; /* Open connection */ assert(p_sys->stream == NULL); /* No open sockets (leaking fds is BAD) */ p_sys->stream = vlc_tls_SocketOpenTCP(VLC_OBJECT(p_access), srv.psz_host, srv.i_port); if (p_sys->stream == NULL) { msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port ); free( stream.ptr ); return -1; } msg_Dbg( p_access, "sending request:\n%s", stream.ptr ); val = vlc_tls_Write(p_sys->stream, stream.ptr, stream.length); free( stream.ptr ); if( val < (ssize_t)stream.length ) { msg_Err( p_access, "failed to send request" ); Disconnect( p_access ); return -2; } /* Read Answer */ char *psz = vlc_tls_GetLine(p_sys->stream); if( psz == NULL ) { msg_Err( p_access, "failed to read answer" ); goto error; } if( !strncmp( psz, "HTTP/1.", 7 ) ) { p_sys->i_code = atoi( &psz[9] ); msg_Dbg( p_access, "HTTP answer code %d", p_sys->i_code ); } else if( !strncmp( psz, "ICY", 3 ) ) { p_sys->i_code = atoi( &psz[4] ); msg_Dbg( p_access, "ICY answer code %d", p_sys->i_code ); p_sys->b_icecast = true; p_sys->b_reconnect = true; } else { msg_Err( p_access, "invalid HTTP reply '%s'", psz ); free( psz ); goto error; } /* Authentication error - We'll have to display the dialog */ if( p_sys->i_code == 401 ) { } /* Other fatal error */ else if( p_sys->i_code >= 400 ) { msg_Err( p_access, "error: %s", psz ); free( psz ); goto error; } free( psz ); for( ;; ) { char *p, *p_trailing; psz = vlc_tls_GetLine(p_sys->stream); if( psz == NULL ) { msg_Err( p_access, "failed to read answer" ); goto error; } /* msg_Dbg( p_input, "Line=%s", psz ); */ if( *psz == '\0' ) { free( psz ); break; } if( ( p = strchr( psz, ':' ) ) == NULL ) { msg_Err( p_access, "malformed header line: %s", psz ); free( psz ); goto error; } *p++ = '\0'; p += strspn( p, " \t" ); /* trim trailing white space */ p_trailing = p + strlen( p ); if( p_trailing > p ) { p_trailing--; while( ( *p_trailing == ' ' || *p_trailing == '\t' ) && p_trailing > p ) { *p_trailing = '\0'; p_trailing--; } } if( !strcasecmp( psz, "Content-Length" ) ) { uint64_t i_size = (uint64_t)atoll( p ); if(i_size > p_sys->size) { p_sys->b_has_size = true; p_sys->size = i_size; } } else if( !strcasecmp( psz, "Location" ) ) { char * psz_new_loc; /* This does not follow RFC 2068, but yet if the url is not absolute, * handle it as everyone does. */ if( p[0] == '/' ) { if( p_sys->url.i_port == 80 ) { if( asprintf(&psz_new_loc, "http://%s%s", p_sys->url.psz_host, p) < 0 ) goto error; } else { if( asprintf(&psz_new_loc, "http://%s:%d%s", p_sys->url.psz_host, p_sys->url.i_port, p) < 0 ) goto error; } } else { psz_new_loc = strdup( p ); } free( p_sys->psz_location ); p_sys->psz_location = psz_new_loc; } else if( !strcasecmp( psz, "Content-Type" ) ) { free( p_sys->psz_mime ); p_sys->psz_mime = strdup( p ); msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime ); } else if( !strcasecmp( psz, "Content-Encoding" ) ) { msg_Dbg( p_access, "Content-Encoding: %s", p ); } else if( !strcasecmp( psz, "Server" ) ) { msg_Dbg( p_access, "Server: %s", p ); if( !strncasecmp( p, "Icecast", 7 ) || !strncasecmp( p, "Nanocaster", 10 ) ) { /* Remember if this is Icecast * we need to force demux in this case without breaking * autodetection */ /* Let live 365 streams (nanocaster) piggyback on the icecast * routine. They look very similar */ p_sys->b_reconnect = true; p_sys->b_icecast = true; } } else if( !strcasecmp( psz, "Icy-MetaInt" ) ) { msg_Dbg( p_access, "Icy-MetaInt: %s", p ); p_sys->i_icy_meta = atoi( p ); if( p_sys->i_icy_meta < 0 ) p_sys->i_icy_meta = 0; if( p_sys->i_icy_meta > 1 ) { p_sys->i_icy_offset = p_sys->i_icy_meta; p_sys->b_icecast = true; } msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta ); } else if( !strcasecmp( psz, "Icy-Name" ) ) { free( p_sys->psz_icy_name ); char *psz_tmp = strdup( p ); p_sys->psz_icy_name = EnsureUTF8( psz_tmp ); if( !p_sys->psz_icy_name ) free( psz_tmp ); else vlc_xml_decode( p_sys->psz_icy_name ); msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name ); if ( p_access->p_input_item ) input_item_SetMeta( p_access->p_input_item, vlc_meta_Title, p_sys->psz_icy_name ); p_sys->b_icecast = true; /* be on the safeside. set it here as well. */ p_sys->b_reconnect = true; } else if( !strcasecmp( psz, "Icy-Genre" ) ) { free( p_sys->psz_icy_genre ); char *psz_tmp = strdup( p ); p_sys->psz_icy_genre = EnsureUTF8( psz_tmp ); if( !p_sys->psz_icy_genre ) free( psz_tmp ); else vlc_xml_decode( p_sys->psz_icy_genre ); msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre ); if( p_access->p_input_item ) input_item_SetMeta( p_access->p_input_item, vlc_meta_Genre, p_sys->psz_icy_genre ); } else if( !strncasecmp( psz, "Icy-Notice", 10 ) ) { msg_Dbg( p_access, "Icy-Notice: %s", p ); } else if( !strncasecmp( psz, "icy-", 4 ) || !strncasecmp( psz, "ice-", 4 ) || !strncasecmp( psz, "x-audiocast", 11 ) ) { msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p ); } else if( !strcasecmp( psz, "www-authenticate" ) ) { msg_Dbg( p_access, "Authentication header: %s", p ); vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access), &p_sys->auth, p ); } else if( !strcasecmp( psz, "proxy-authenticate" ) ) { msg_Dbg( p_access, "Proxy authentication header: %s", p ); vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access), &p_sys->proxy_auth, p ); } else if( !strcasecmp( psz, "authentication-info" ) ) { msg_Dbg( p_access, "Authentication Info header: %s", p ); if( AuthCheckReply( p_access, p, &p_sys->url, &p_sys->auth ) ) goto error; } else if( !strcasecmp( psz, "proxy-authentication-info" ) ) { msg_Dbg( p_access, "Proxy Authentication Info header: %s", p ); if( AuthCheckReply( p_access, p, &p_sys->proxy, &p_sys->proxy_auth ) ) goto error; } free( psz ); } return 0; error: Disconnect( p_access ); return -2; }
/* takes all values in the values of vlc_keystore_entry * * and formats them in a url key */ static char* values2key( const char* const* ppsz_values, bool b_search ) { char* psz_b64_realm = NULL; char* psz_b64_auth = NULL; bool b_state = false; if ( ( !ppsz_values[KEY_PROTOCOL] || !ppsz_values[KEY_SERVER] ) && !b_search ) return NULL; struct vlc_memstream ms; if ( vlc_memstream_open( &ms ) ) return NULL; /* Protocol section */ if ( ppsz_values[KEY_PROTOCOL] ) vlc_memstream_printf( &ms, "%s://", ppsz_values[KEY_PROTOCOL] ); else if ( b_search ) vlc_memstream_printf( &ms, "*://" ); /* User section */ if ( ppsz_values[KEY_USER] ) vlc_memstream_printf( &ms, "%s@", ppsz_values[KEY_USER] ); else if ( b_search ) vlc_memstream_printf( &ms, "*" ); /* Server section */ if ( ppsz_values[KEY_SERVER] ) vlc_memstream_printf( &ms, "%s", ppsz_values[KEY_SERVER] ); else if ( b_search ) vlc_memstream_printf( &ms, "*" ); /* Port section */ if ( ppsz_values[KEY_PORT] ) vlc_memstream_printf( &ms, ":%s", ppsz_values[KEY_PORT] ); else if ( b_search ) vlc_memstream_printf( &ms, "*" ); /* Path section */ if( ppsz_values[KEY_PATH] ) { if( ppsz_values[KEY_PATH][0] != '/' ) vlc_memstream_putc( &ms, '/' ); vlc_memstream_puts( &ms, ppsz_values[KEY_PATH] ); } else if ( b_search ) vlc_memstream_printf( &ms, "*" ); /* Realm and authtype section */ if ( ppsz_values[KEY_REALM] || ppsz_values[KEY_AUTHTYPE] || b_search ) { vlc_memstream_printf( &ms, "?" ); /* Realm section */ if ( ppsz_values[KEY_REALM] || b_search ) { if ( ppsz_values[KEY_REALM] ) { psz_b64_realm = vlc_b64_encode_binary( ( uint8_t* )ppsz_values[KEY_REALM], strlen(ppsz_values[KEY_REALM] ) ); if ( !psz_b64_realm ) goto end; vlc_memstream_printf( &ms, "realm=%s", psz_b64_realm ); } else vlc_memstream_printf( &ms, "*" ); if ( ppsz_values[KEY_AUTHTYPE] ) vlc_memstream_printf( &ms, "&" ); } /* Authtype section */ if ( ppsz_values[KEY_AUTHTYPE] || b_search ) { if ( ppsz_values[KEY_AUTHTYPE] ) { psz_b64_auth = vlc_b64_encode_binary( ( uint8_t* )ppsz_values[KEY_AUTHTYPE], strlen(ppsz_values[KEY_AUTHTYPE] ) ); if ( !psz_b64_auth ) goto end; vlc_memstream_printf( &ms, "authtype=%s", psz_b64_auth ); } else vlc_memstream_printf( &ms, "*" ); } } b_state = true; end: free( psz_b64_realm ); free( psz_b64_auth ); if ( vlc_memstream_flush( &ms ) != 0 ) b_state = false; char *psz_key = vlc_memstream_close( &ms ) == 0 ? ms.ptr : NULL; if ( !b_state ) { free( psz_key ); psz_key = NULL; } return psz_key; }