static int vlclua_httpd_handler_new( lua_State * L ) { httpd_host_t **pp_host = (httpd_host_t **)luaL_checkudata( L, 1, "httpd_host" ); const char *psz_url = luaL_checkstring( L, 2 ); const char *psz_user = luaL_nilorcheckstring( L, 3 ); const char *psz_password = luaL_nilorcheckstring( L, 4 ); const vlc_acl_t **pp_acl = lua_isnil( L, 5 ) ? NULL : luaL_checkudata( L, 5, "acl" ); /* Stack item 6 is the callback function */ luaL_argcheck( L, lua_isfunction( L, 6 ), 6, "Should be a function" ); /* Stack item 7 is the callback data */ lua_settop( L, 7 ); httpd_handler_sys_t *p_sys = (httpd_handler_sys_t*) malloc( sizeof( httpd_handler_sys_t ) ); if( !p_sys ) return luaL_error( L, "Failed to allocate private buffer." ); p_sys->L = lua_newthread( L ); p_sys->ref = luaL_ref( L, LUA_REGISTRYINDEX ); /* pops the object too */ /* use lua_xmove to move the lua callback function and data to * the callback's stack. */ lua_xmove( L, p_sys->L, 2 ); httpd_handler_t *p_handler = httpd_HandlerNew( *pp_host, psz_url, psz_user, psz_password, pp_acl?*pp_acl:NULL, vlclua_httpd_handler_callback, p_sys ); if( !p_handler ) { free( p_sys ); return luaL_error( L, "Failed to create HTTPd handler." ); } httpd_handler_t **pp_handler = lua_newuserdata( L, sizeof( httpd_handler_t * ) ); *pp_handler = p_handler; if( luaL_newmetatable( L, "httpd_handler" ) ) { lua_pushcfunction( L, vlclua_httpd_handler_delete ); lua_setfield( L, -2, "__gc" ); } lua_setmetatable( L, -2 ); return 1; }
/***************************************************************************** * Activate: initialize and create stuff *****************************************************************************/ static int Open( vlc_object_t *p_this ) { intf_thread_t *p_intf = (intf_thread_t*)p_this; intf_sys_t *p_sys; char *psz_address; char *psz_cert = NULL, *psz_key = NULL, *psz_ca = NULL, *psz_crl = NULL; int i_port = 0; char *psz_src = NULL; psz_address = var_CreateGetNonEmptyString( p_intf, "http-host" ); if( psz_address != NULL ) { char *psz_parser = strrchr( psz_address, ':' ); if( psz_parser ) { *psz_parser++ = '\0'; i_port = atoi( psz_parser ); } } else psz_address = strdup(""); p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) ); if( !p_intf->p_sys ) { free( psz_address ); return( VLC_ENOMEM ); } p_sys->p_playlist = pl_Get( p_this ); p_sys->p_input = NULL; p_sys->p_vlm = NULL; p_sys->psz_address = psz_address; p_sys->i_port = i_port; p_sys->p_art_handler = NULL; /* determine file handler associations */ p_sys->i_handlers = 0; p_sys->pp_handlers = NULL; #if defined( HAVE_FORK ) || defined( WIN32 ) psz_src = var_InheritString( p_intf, "http-handlers" ); if( psz_src != NULL ) { char *p = psz_src; while( p != NULL ) { http_association_t *p_handler; char *psz_ext = p; char *psz_program, *psz_options; p = strchr( p, '=' ); if( p == NULL ) break; *p++ = '\0'; psz_program = p; p = strchr( p, ',' ); if( p != NULL ) *p++ = '\0'; p_handler = malloc( sizeof( http_association_t ) ); p_handler->psz_ext = strdup( psz_ext ); psz_options = FirstWord( psz_program, psz_program ); p_handler->i_argc = 0; p_handler->ppsz_argv = NULL; TAB_APPEND( p_handler->i_argc, p_handler->ppsz_argv, strdup( psz_program ) ); while( psz_options != NULL && *psz_options ) { char *psz_next = FirstWord( psz_options, psz_options ); TAB_APPEND( p_handler->i_argc, p_handler->ppsz_argv, strdup( psz_options ) ); psz_options = psz_next; } /* NULL will be appended later on */ TAB_APPEND( p_sys->i_handlers, p_sys->pp_handlers, p_handler ); } free( psz_src ); } #endif /* determine SSL configuration */ psz_cert = var_InheritString( p_intf, "http-intf-cert" ); if ( psz_cert != NULL ) { msg_Dbg( p_intf, "enabling TLS for HTTP interface (cert file: %s)", psz_cert ); psz_key = var_InheritString( p_intf, "http-intf-key" ); psz_ca = var_InheritString( p_intf, "http-intf-ca" ); psz_crl = var_InheritString( p_intf, "http-intf-crl" ); if( i_port <= 0 ) i_port = 8443; } else { if( i_port <= 0 ) i_port= 8080; } msg_Dbg( p_intf, "base %s:%d", psz_address, i_port ); p_sys->p_httpd_host = httpd_TLSHostNew( VLC_OBJECT(p_intf), psz_address, i_port, psz_cert, psz_key, psz_ca, psz_crl ); free( psz_cert ); free( psz_key ); free( psz_ca ); free( psz_crl ); if( p_sys->p_httpd_host == NULL ) { msg_Err( p_intf, "cannot listen on %s:%d", psz_address, i_port ); free( p_sys->psz_address ); free( p_sys ); return VLC_EGENERIC; } else { char psz_tmp[NI_MAXHOST + 6]; /* Ugly hack to run several HTTP servers on different ports */ snprintf( psz_tmp, sizeof (psz_tmp), "%s:%d", psz_address, i_port + 1 ); var_Create(p_intf->p_libvlc, "http-host", VLC_VAR_STRING ); var_SetString( p_intf->p_libvlc, "http-host", psz_tmp ); } p_sys->i_files = 0; p_sys->pp_files = NULL; psz_src = var_InheritString( p_intf, "http-src" ); if( psz_src == NULL ) { char *data_path = config_GetDataDir( p_intf ); if( asprintf( &psz_src, "%s" DIR_SEP "http", data_path ) == -1 ) psz_src = NULL; free( data_path ); } if( psz_src == NULL ) { msg_Err( p_intf, "invalid web interface source directory" ); goto failed; } /* remove trainling \ or / */ if( psz_src[strlen( psz_src ) - 1] == '\\' || psz_src[strlen( psz_src ) - 1] == '/' ) { psz_src[strlen( psz_src ) - 1] = '\0'; } ParseDirectory( p_intf, psz_src, psz_src ); if( p_sys->i_files <= 0 ) { msg_Err( p_intf, "cannot find any file in directory %s", psz_src ); goto failed; } if( var_InheritBool( p_intf, "http-album-art" ) ) { /* FIXME: we're leaking h */ httpd_handler_sys_t *h = malloc( sizeof( httpd_handler_sys_t ) ); if( !h ) goto failed; h->file.p_intf = p_intf; h->file.file = NULL; h->file.name = NULL; /* TODO: use ACL and login/password stuff here too */ h->p_handler = httpd_HandlerNew( p_sys->p_httpd_host, "/art", NULL, NULL, NULL, ArtCallback, h ); p_sys->p_art_handler = h->p_handler; } free( psz_src ); return VLC_SUCCESS; failed: free( psz_src ); free( p_sys->pp_files ); httpd_HostDelete( p_sys->p_httpd_host ); free( p_sys->psz_address ); free( p_sys ); return VLC_EGENERIC; }
/* Parse a directory and recursively add files */ int ParseDirectory( intf_thread_t *p_intf, char *psz_root, char *psz_dir ) { intf_sys_t *p_sys = p_intf->p_sys; char dir[MAX_DIR_SIZE]; DIR *p_dir; vlc_acl_t *p_acl; FILE *file; char *user = NULL; char *password = NULL; int i_dirlen; if( ( p_dir = utf8_opendir( psz_dir ) ) == NULL ) { if( errno != ENOENT && errno != ENOTDIR ) msg_Err( p_intf, "cannot open directory (%s)", psz_dir ); return VLC_EGENERIC; } i_dirlen = strlen( psz_dir ); if( i_dirlen + 10 > MAX_DIR_SIZE ) { msg_Warn( p_intf, "skipping too deep directory (%s)", psz_dir ); closedir( p_dir ); return 0; } msg_Dbg( p_intf, "dir=%s", psz_dir ); snprintf( dir, sizeof( dir ), "%s"DIR_SEP".access", psz_dir ); if( ( file = utf8_fopen( dir, "r" ) ) != NULL ) { char line[1024]; int i_size; msg_Dbg( p_intf, "find .access in dir=%s", psz_dir ); i_size = fread( line, 1, 1023, file ); if( i_size > 0 ) { char *p; while( i_size > 0 && ( line[i_size-1] == '\n' || line[i_size-1] == '\r' ) ) { i_size--; } line[i_size] = '\0'; p = strchr( line, ':' ); if( p ) { *p++ = '\0'; user = strdup( line ); password = strdup( p ); } } msg_Dbg( p_intf, "using user=%s (read=%d)", user, i_size ); fclose( file ); } snprintf( dir, sizeof( dir ), "%s"DIR_SEP".hosts", psz_dir ); p_acl = ACL_Create( p_intf, false ); if( ACL_LoadFile( p_acl, dir ) ) { ACL_Destroy( p_acl ); struct stat st; if( utf8_stat( dir, &st ) == 0 ) { free( user ); free( password ); closedir( p_dir ); return VLC_EGENERIC; } p_acl = NULL; } for( ;; ) { char *psz_filename; /* parse psz_src dir */ if( ( psz_filename = utf8_readdir( p_dir ) ) == NULL ) { break; } if( ( psz_filename[0] == '.' ) || ( i_dirlen + strlen( psz_filename ) > MAX_DIR_SIZE ) ) { free( psz_filename ); continue; } snprintf( dir, sizeof( dir ), "%s"DIR_SEP"%s", psz_dir, psz_filename ); free( psz_filename ); if( ParseDirectory( p_intf, psz_root, dir ) ) { httpd_file_sys_t *f = NULL; httpd_handler_sys_t *h = NULL; bool b_index; char *psz_name, *psz_ext; psz_name = FileToUrl( &dir[strlen( psz_root )], &b_index ); psz_ext = strrchr( dir, '.' ); if( psz_ext != NULL ) { int i; psz_ext++; for( i = 0; i < p_sys->i_handlers; i++ ) if( !strcmp( p_sys->pp_handlers[i]->psz_ext, psz_ext ) ) break; if( i < p_sys->i_handlers ) { f = malloc( sizeof( httpd_handler_sys_t ) ); h = (httpd_handler_sys_t *)f; f->b_handler = true; h->p_association = p_sys->pp_handlers[i]; } } if( f == NULL ) { f = malloc( sizeof( httpd_file_sys_t ) ); f->b_handler = false; } f->p_intf = p_intf; f->p_file = NULL; f->p_redir = NULL; f->p_redir2 = NULL; f->file = strdup (dir); f->name = psz_name; f->b_html = strstr( &dir[strlen( psz_root )], ".htm" ) || strstr( &dir[strlen( psz_root )], ".xml" ) ? true : false; if( !f->name ) { msg_Err( p_intf , "unable to parse directory" ); closedir( p_dir ); free( f ); return( VLC_ENOMEM ); } msg_Dbg( p_intf, "file=%s (url=%s)", f->file, f->name ); if( !f->b_handler ) { char *psz_type = strdup( "text/html; charset=UTF-8" ); if( strstr( &dir[strlen( psz_root )], ".xml" ) ) { char *psz = strstr( psz_type, "html;" ); if( psz ) { psz[0] = 'x'; psz[1] = 'm'; psz[2] = 'l'; psz[3] = ';'; psz[4] = ' '; } } f->p_file = httpd_FileNew( p_sys->p_httpd_host, f->name, f->b_html ? psz_type : NULL, user, password, p_acl, HttpCallback, f ); free( psz_type ); if( f->p_file != NULL ) { TAB_APPEND( p_sys->i_files, p_sys->pp_files, f ); } } else { h->p_handler = httpd_HandlerNew( p_sys->p_httpd_host, f->name, user, password, p_acl, HandlerCallback, h ); if( h->p_handler != NULL ) { TAB_APPEND( p_sys->i_files, p_sys->pp_files, (httpd_file_sys_t *)h ); } } /* for url that ends by / add * - a redirect from rep to rep/ * - in case of index.* rep/index.html to rep/ */ if( f && f->name[strlen(f->name) - 1] == '/' ) { char *psz_redir = strdup( f->name ); char *p; psz_redir[strlen( psz_redir ) - 1] = '\0'; msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name ); f->p_redir = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir ); free( psz_redir ); if( b_index && ( p = strstr( f->file, "index." ) ) ) { if( asprintf( &psz_redir, "%s%s", f->name, p ) != -1 ) { msg_Dbg( p_intf, "redir=%s -> %s", psz_redir, f->name ); f->p_redir2 = httpd_RedirectNew( p_sys->p_httpd_host, f->name, psz_redir ); free( psz_redir ); } } } } } free( user ); free( password ); ACL_Destroy( p_acl ); closedir( p_dir ); return VLC_SUCCESS; }