static int vlclua_encode_uri_component( lua_State *L ) { int i_top = lua_gettop( L ); int i; for( i = 1; i <= i_top; i++ ) { const char *psz_cstring = luaL_checkstring( L, 1 ); char *psz_string = encode_URI_component( psz_cstring ); lua_remove( L,1 ); lua_pushstring( L, psz_string ); free( psz_string ); } return i_top; }
/** * Add the directory part of the playlist file to the start of the * mrl, if the mrl is a relative file path */ char *ProcessMRL( const char *psz_mrl, const char *psz_prefix ) { /* Check for a protocol name. * for URL, we should look for "://" * for MRL (Media Resource Locator) ([[<access>][/<demux>]:][<source>]), * we should look for ":", so we end up looking simply for ":" * PB: on some file systems, ':' are valid characters though */ /* Simple cases first */ if( !psz_mrl || !*psz_mrl ) return NULL; if( !psz_prefix || !*psz_prefix ) goto uri; /* Check if the line specifies an absolute path */ /* FIXME: that's wrong if the playlist is not a local file */ if( *psz_mrl == DIR_SEP_CHAR ) goto uri; #ifdef WIN32 /* Drive letter (this assumes URL scheme are not a single character) */ if( isalpha(psz_mrl[0]) && psz_mrl[1] == ':' ) goto uri; #endif if( strstr( psz_mrl, "://" ) ) return strdup( psz_mrl ); /* This a relative path, prepend the prefix */ char *ret; char *postfix = encode_URI_component( psz_mrl ); /* FIXME: postfix may not be encoded correctly (esp. slashes) */ if( postfix == NULL || asprintf( &ret, "%s%s", psz_prefix, postfix ) == -1 ) ret = NULL; free( postfix ); return ret; uri: return make_URI( psz_mrl, NULL ); }
block_t *DirBlock (access_t *p_access) { access_sys_t *p_sys = p_access->p_sys; directory_t *current = p_sys->current; if (p_access->info.b_eof) return NULL; if (current == NULL) { /* Startup: send the XSPF header */ static const char header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n" " <trackList>\n"; block_t *block = block_Alloc (sizeof (header) - 1); if (!block) goto fatal; memcpy (block->p_buffer, header, sizeof (header) - 1); /* "Open" the base directory */ current = malloc (sizeof (*current)); if (current == NULL) { block_Release (block); goto fatal; } current->parent = NULL; current->handle = p_sys->handle; #ifndef HAVE_OPENAT current->path = strdup (p_access->psz_filepath); #endif current->uri = p_sys->uri; if (fstat (dirfd (current->handle), ¤t->st)) { free (current); block_Release (block); goto fatal; } p_sys->handle = NULL; p_sys->uri = NULL; p_sys->current = current; return block; } char *entry = vlc_readdir (current->handle); if (entry == NULL) { /* End of directory, go back to parent */ closedir (current->handle); p_sys->current = current->parent; free (current->uri); #ifndef HAVE_OPENAT free (current->path); #endif free (current); if (p_sys->current == NULL) { /* End of XSPF playlist */ char *footer; int len = asprintf( &footer, " </trackList>\n" \ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \ "%s" \ " </extension>\n" \ "</playlist>\n", p_sys->psz_xspf_extension ); if (unlikely(len == -1)) goto fatal; block_t *block = block_heap_Alloc (footer, footer, len); if (unlikely(block == NULL)) free (footer); p_access->info.b_eof = true; return block; } else { /* This was the end of a "subnode" */ /* Write the ID to the extension */ char *old_xspf_extension = p_sys->psz_xspf_extension; if (old_xspf_extension == NULL) goto fatal; int len2 = asprintf( &p_sys->psz_xspf_extension, "%s </vlc:node>\n", old_xspf_extension ); if (len2 == -1) goto fatal; free( old_xspf_extension ); } return NULL; } /* Skip current, parent and hidden directories */ if (entry[0] == '.') { free (entry); return NULL; } /* Handle recursion */ if (p_sys->mode != MODE_COLLAPSE) { directory_t *sub = malloc (sizeof (*sub)); if (sub == NULL) { free (entry); return NULL; } DIR *handle; #ifdef HAVE_OPENAT int fd = vlc_openat (dirfd (current->handle), entry, O_RDONLY); if (fd != -1) { handle = fdopendir (fd); if (handle == NULL) close (fd); } else handle = NULL; #else if (asprintf (&sub->path, "%s/%s", current->path, entry) != -1) handle = vlc_opendir (sub->path); else handle = NULL; #endif if (handle != NULL) { sub->parent = current; sub->handle = handle; char *encoded = encode_URI_component (entry); if ((encoded == NULL) || (asprintf (&sub->uri, "%s/%s", current->uri, encoded) == -1)) sub->uri = NULL; free (encoded); if ((p_sys->mode == MODE_NONE) || fstat (dirfd (handle), &sub->st) || has_inode_loop (sub) || (sub->uri == NULL)) { free (entry); closedir (handle); free (sub->uri); free (sub); return NULL; } p_sys->current = sub; /* Add node to xspf extension */ char *old_xspf_extension = p_sys->psz_xspf_extension; if (old_xspf_extension == NULL) { free (entry); goto fatal; } char *title = convert_xml_special_chars (entry); free (entry); if (title == NULL || asprintf (&p_sys->psz_xspf_extension, "%s" " <vlc:node title=\"%s\">\n", old_xspf_extension, title) == -1) { free (title); goto fatal; } free (title); free (old_xspf_extension); return NULL; } else free (sub); } /* Skip files with ignored extensions */ if (p_sys->ignored_exts != NULL) { const char *ext = strrchr (entry, '.'); if (ext != NULL) { size_t extlen = strlen (++ext); for (const char *type = p_sys->ignored_exts, *end; type[0]; type = end + 1) { end = strchr (type, ','); if (end == NULL) end = type + strlen (type); if (type + extlen == end && !strncasecmp (ext, type, extlen)) { free (entry); return NULL; } if (*end == '\0') break; } } } char *encoded = encode_URI_component (entry); free (entry); if (encoded == NULL) goto fatal; int len = asprintf (&entry, " <track><location>%s/%s</location>\n" \ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \ " <vlc:id>%d</vlc:id>\n" \ " </extension>\n" \ " </track>\n", current->uri, encoded, p_sys->i_item_count++); free (encoded); if (len == -1) goto fatal; /* Write the ID to the extension */ char *old_xspf_extension = p_sys->psz_xspf_extension; if (old_xspf_extension == NULL) goto fatal; int len2 = asprintf( &p_sys->psz_xspf_extension, "%s <vlc:item tid=\"%i\" />\n", old_xspf_extension, p_sys->i_item_count-1 ); if (len2 == -1) goto fatal; free( old_xspf_extension ); block_t *block = block_heap_Alloc (entry, entry, len); if (unlikely(block == NULL)) { free (entry); goto fatal; } return block; fatal: p_access->info.b_eof = true; return NULL; }
block_t *DirBlock (access_t *p_access) { access_sys_t *p_sys = p_access->p_sys; directory_t *current = p_sys->current; if (p_access->info.b_eof) return NULL; if (p_sys->header) { /* Startup: send the XSPF header */ static const char header[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\" xmlns:vlc=\"http://www.videolan.org/vlc/playlist/ns/0/\">\n" " <trackList>\n"; block_t *block = block_Alloc (sizeof (header) - 1); if (!block) goto fatal; memcpy (block->p_buffer, header, sizeof (header) - 1); p_sys->header = false; return block; } if (current->i >= current->filec) { /* End of directory, go back to parent */ closedir (current->handle); p_sys->current = current->parent; free (current->uri); free (current->filev); #ifndef HAVE_OPENAT free (current->path); #endif free (current); if (p_sys->current == NULL) { /* End of XSPF playlist */ char *footer; int len = asprintf (&footer, " </trackList>\n" " <extension application=\"http://www.videolan.org/" "vlc/playlist/0\">\n" "%s" " </extension>\n" "</playlist>\n", p_sys->xspf_ext ? p_sys->xspf_ext : ""); if (unlikely(len == -1)) goto fatal; block_t *block = block_heap_Alloc (footer, len); p_access->info.b_eof = true; return block; } else { /* This was the end of a "subnode" */ /* Write the ID to the extension */ char *old_xspf_ext = p_sys->xspf_ext; if (old_xspf_ext != NULL && asprintf (&p_sys->xspf_ext, "%s </vlc:node>\n", old_xspf_ext ? old_xspf_ext : "") == -1) p_sys->xspf_ext = NULL; free (old_xspf_ext); } return NULL; } char *entry = current->filev[current->i++]; /* Handle recursion */ if (p_sys->mode != MODE_COLLAPSE) { DIR *handle; #ifdef HAVE_OPENAT int fd = vlc_openat (dirfd (current->handle), entry, O_RDONLY | O_DIRECTORY); if (fd == -1) { if (errno == ENOTDIR) goto notdir; goto skip; /* File cannot be opened... forget it */ } struct stat st; if (fstat (fd, &st) || p_sys->mode == MODE_NONE || has_inode_loop (current, st.st_dev, st.st_ino) || (handle = fdopendir (fd)) == NULL) { close (fd); goto skip; } #else char *path; if (asprintf (&path, "%s/%s", current->path, entry) == -1) goto skip; if ((handle = vlc_opendir (path)) == NULL) goto notdir; if (p_sys->mode == MODE_NONE) goto skip; #endif directory_t *sub = malloc (sizeof (*sub)); if (unlikely(sub == NULL)) { closedir (handle); #ifndef HAVE_OPENAT free (path); #endif goto skip; } sub->parent = current; sub->handle = handle; sub->filec = vlc_loaddir (handle, &sub->filev, visible, p_sys->compar); if (sub->filec < 0) sub->filev = NULL; sub->i = 0; #ifdef HAVE_OPENAT sub->device = st.st_dev; sub->inode = st.st_ino; #else sub->path = path; #endif p_sys->current = sub; char *encoded = encode_URI_component (entry); if (encoded == NULL || (asprintf (&sub->uri, "%s/%s", current->uri, encoded) == -1)) sub->uri = NULL; free (encoded); if (unlikely(sub->uri == NULL)) { free (entry); goto fatal; } /* Add node to XSPF extension */ char *old_xspf_ext = p_sys->xspf_ext; EnsureUTF8 (entry); char *title = convert_xml_special_chars (entry); if (old_xspf_ext != NULL && asprintf (&p_sys->xspf_ext, "%s <vlc:node title=\"%s\">\n", old_xspf_ext, title ? title : "?") == -1) p_sys->xspf_ext = NULL; free (old_xspf_ext); free (title); goto skip; } notdir: /* Skip files with ignored extensions */ if (p_sys->ignored_exts != NULL) { const char *ext = strrchr (entry, '.'); if (ext != NULL) { size_t extlen = strlen (++ext); for (const char *type = p_sys->ignored_exts, *end; type[0]; type = end + 1) { end = strchr (type, ','); if (end == NULL) end = type + strlen (type); if (type + extlen == end && !strncasecmp (ext, type, extlen)) { free (entry); return NULL; } if (*end == '\0') break; } } } char *encoded = encode_URI_component (entry); free (entry); if (encoded == NULL) goto fatal; int len = asprintf (&entry, " <track><location>%s/%s</location>\n" \ " <extension application=\"http://www.videolan.org/vlc/playlist/0\">\n" \ " <vlc:id>%d</vlc:id>\n" \ " </extension>\n" \ " </track>\n", current->uri, encoded, p_sys->i_item_count++); free (encoded); if (len == -1) goto fatal; /* Write the ID to the extension */ char *old_xspf_ext = p_sys->xspf_ext; if (old_xspf_ext != NULL && asprintf (&p_sys->xspf_ext, "%s <vlc:item tid=\"%i\" />\n", old_xspf_ext, p_sys->i_item_count - 1) == -1) p_sys->xspf_ext = NULL; free (old_xspf_ext); block_t *block = block_heap_Alloc (entry, len); if (unlikely(block == NULL)) goto fatal; return block; fatal: p_access->info.b_eof = true; return NULL; skip: free (entry); return NULL; }
int RarStreamOpen(vlc_object_t *object) { stream_t *s = (stream_t*)object; if (RarProbe(s->p_source)) return VLC_EGENERIC; int count; rar_file_t **files; const int64_t position = stream_Tell(s->p_source); if ((RarParse(s->p_source, &count, &files, false) && RarParse(s->p_source, &count, &files, true )) || count == 0 ) { stream_Seek(s->p_source, position); msg_Info(s, "Invalid or unsupported RAR archive"); free(files); return VLC_EGENERIC; } /* TODO use xspf to have node for directories * Reusing WriteXSPF from the zip access is probably a good idea * (becareful about '\' and '/'. */ char *mrl; if (asprintf(&mrl, "%s://%s", s->psz_access, s->psz_path)< 0) mrl = NULL; char *base; char *encoded = mrl ? encode_URI_component(mrl) : NULL; free(mrl); if (!encoded || asprintf(&base, "rar://%s", encoded) < 0) base = NULL; free(encoded); char *data = strdup("#EXTM3U\n"); for (int i = 0; i < count; i++) { rar_file_t *f = files[i]; char *next; if (base && data && asprintf(&next, "%s" "#EXTINF:,,%s\n" "%s|%s\n", data, f->name, base, f->name) >= 0) { free(data); data = next; } RarFileDelete(f); } free(base); free(files); if (!data) return VLC_EGENERIC; stream_t *payload = stream_MemoryNew(s, (uint8_t*)data, strlen(data), false); if (!payload) { free(data); return VLC_EGENERIC; } s->pf_read = Read; s->pf_peek = Peek; s->pf_control = Control; stream_sys_t *sys = s->p_sys = malloc(sizeof(*sys)); if (!sys) { stream_Delete(payload); return VLC_ENOMEM; } sys->payload = payload; char *tmp; if (asprintf(&tmp, "%s.m3u", s->psz_path) < 0) { RarStreamClose(object); return VLC_ENOMEM; } free(s->psz_path); s->psz_path = tmp; return VLC_SUCCESS; }
/** * @brief Scan files in a particular directory */ static void ScanFiles( monitoring_thread_t *p_mon, int i_dir_id, bool b_recursive, stat_list_t *stparent ) { int i_rows, i_cols, i_dir_content, i, i_mon_rows, i_mon_cols; char **ppsz_monitored_files; char **pp_results, *psz_dir; char **pp_dir_content; bool *pb_processed; input_item_t *p_input; struct stat s_stat; media_library_t *p_ml = (media_library_t *)p_mon->p_ml; Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT uri AS directory_uri FROM directories WHERE id = '%d'", i_dir_id ); if( i_rows < 1 ) { msg_Dbg( p_mon, "query returned no directory for dir_id: %d (%s:%d)", i_dir_id, __FILE__, __LINE__ ); return; } psz_dir = strdup( pp_results[1] ); FreeSQLResult( p_ml, pp_results ); struct stat_list_t stself; if( vlc_stat( psz_dir, &stself.st ) == -1 ) { msg_Err( p_ml, "Cannot stat `%s': %m", psz_dir ); free( psz_dir ); return; } #ifndef WIN32 for( stat_list_t *stats = stparent; stats != NULL; stats = stats->parent ) { if( ( stself.st.st_ino == stats->st.st_ino ) && ( stself.st.st_dev == stats->st.st_dev ) ) { msg_Warn( p_ml, "Ignoring infinitely recursive directory `%s'", psz_dir ); free( psz_dir ); return; } } #else /* Windows has st_dev (driver letter - 'A'), but it zeroes st_ino, * so that the test above will always incorrectly succeed. * Besides, Windows does not have dirfd(). */ #endif stself.parent = stparent; QuerySimple( p_ml, "UPDATE directories SET timestamp=%d WHERE id = %d", stself.st.st_mtime, i_dir_id ); Query( p_ml, &ppsz_monitored_files, &i_mon_rows, &i_mon_cols, "SELECT id AS media_id, timestamp AS media_ts, uri AS media_uri " "FROM media WHERE directory_id = %d", i_dir_id ); pb_processed = malloc(sizeof(bool) * i_mon_rows); for( i = 0; i < i_mon_rows ; i++) pb_processed[i] = false; i_dir_content = vlc_scandir( psz_dir, &pp_dir_content, NULL, Sort ); if( i_dir_content == -1 ) { msg_Err( p_mon, "Cannot read `%s': %m", psz_dir ); free( pb_processed ); free( psz_dir ); return; } else if( i_dir_content == 0 ) { msg_Dbg( p_mon, "Nothing in directory `%s'", psz_dir ); free( pb_processed ); free( psz_dir ); return; } for( i = 0; i < i_dir_content; i++ ) { const char *psz_entry = pp_dir_content[i]; if( psz_entry[0] != '.' ) { /* 7 is the size of "file://" */ char psz_uri[strlen(psz_dir) + strlen(psz_entry) + 2 + 7]; sprintf( psz_uri, "%s/%s", psz_dir, psz_entry ); if( vlc_stat( psz_uri, &s_stat ) == -1 ) { msg_Err( p_mon, "%s: %m", psz_uri ); free( pb_processed ); free( psz_dir ); return; } if( S_ISREG( s_stat.st_mode ) ) { const char *psz_dot = strrchr( psz_uri, '.' ); if( psz_dot++ && *psz_dot ) { int i_is_media = 0; for( int a = 0; ppsz_MediaExtensions[a]; a++ ) { if( !strcasecmp( psz_dot, ppsz_MediaExtensions[a] ) ) { i_is_media = 1; break; } } if( !i_is_media ) { msg_Dbg( p_mon, "ignoring file %s", psz_uri ); continue; } } char * psz_tmp = encode_URI_component( psz_uri ); char * psz_encoded_uri = ( char * )calloc( strlen( psz_tmp ) + 9, 1 ); strcpy( psz_encoded_uri, "file:///" ); strcat( psz_encoded_uri, psz_tmp ); free( psz_tmp ); /* Check if given media is already in DB and it has been updated */ bool b_skip = false; bool b_update = false; int j = 1; for( j = 1; j <= i_mon_rows; j++ ) { if( strcasecmp( ppsz_monitored_files[ j * i_mon_cols + 2 ], psz_encoded_uri ) != 0 ) continue; b_update = true; pb_processed[ j - 1 ] = true; if( atoi( ppsz_monitored_files[ j * i_mon_cols + 1 ] ) < s_stat.st_mtime ) { b_skip = false; break; } else { b_skip = true; break; } } msg_Dbg( p_ml , "Checking if %s is in DB. Found: %d", psz_encoded_uri, b_skip? 1 : 0 ); if( b_skip ) continue; p_input = input_item_New( psz_encoded_uri, psz_entry ); playlist_t* p_pl = pl_Get( p_mon ); preparsed_item_t* p_itemobject; p_itemobject = malloc( sizeof( preparsed_item_t ) ); p_itemobject->i_dir_id = i_dir_id; p_itemobject->psz_uri = psz_encoded_uri; p_itemobject->i_mtime = s_stat.st_mtime; p_itemobject->p_mon = p_mon; p_itemobject->b_update = b_update; p_itemobject->i_update_id = b_update ? atoi( ppsz_monitored_files[ j * i_mon_cols + 0 ] ) : 0 ; vlc_event_manager_t *p_em = &p_input->event_manager; vlc_event_attach( p_em, vlc_InputItemPreparsedChanged, PreparseComplete, p_itemobject ); playlist_PreparseEnqueue( p_pl, p_input ); } else if( S_ISDIR( s_stat.st_mode ) && b_recursive ) { Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT id AS directory_id FROM directories " "WHERE uri=%Q", psz_uri ); FreeSQLResult( p_ml, pp_results ); if( i_rows <= 0 ) { msg_Dbg( p_mon, "New directory `%s' in dir of id %d", psz_uri, i_dir_id ); QuerySimple( p_ml, "INSERT INTO directories (uri, timestamp, " "recursive) VALUES(%Q, 0, 1)", psz_uri ); // We get the id of the directory we've just added Query( p_ml, &pp_results, &i_rows, &i_cols, "SELECT id AS directory_id FROM directories WHERE uri=%Q", psz_uri ); if( i_rows <= 0 ) { msg_Err( p_mon, "Directory `%s' was not sucessfully" " added to the database", psz_uri ); FreeSQLResult( p_ml, pp_results ); continue; } ScanFiles( p_mon, atoi( pp_results[1] ), b_recursive, &stself ); FreeSQLResult( p_ml, pp_results ); } } } } vlc_array_t* delete_ids = vlc_array_new(); for( i = 0; i < i_mon_rows; i++ ) { if( !pb_processed[i] ) { /* This file doesn't exist anymore. Let's...urm...delete it. */ ml_element_t* find = ( ml_element_t* ) calloc( 1, sizeof( ml_element_t ) ); find->criteria = ML_ID; find->value.i = atoi( ppsz_monitored_files[ (i + 1) * i_mon_cols ] ); vlc_array_append( delete_ids, find ); } } /* Delete the unfound media */ if( Delete( p_ml, delete_ids ) != VLC_SUCCESS ) msg_Dbg( p_ml, "Something went wrong in multi delete" ); for( i = 0; i < vlc_array_count( delete_ids ); i++ ) { free( vlc_array_item_at_index( delete_ids, i ) ); } vlc_array_destroy( delete_ids ); FreeSQLResult( p_ml, ppsz_monitored_files ); for( i = 0; i < i_dir_content; i++ ) free( pp_dir_content[i] ); free( pp_dir_content ); free( psz_dir ); free( pb_processed ); }