Unsorted directory listing in PHP - php

This is my code:
$ost=$_GET['id']; //get the ID from the URL
$path = "audio/soundtracks/$ost"; //use the ID to select a path
// Open the folder
$dir_handle = #opendir($path) or die("Unable to open $path");
// Loop through the files
while ($file = readdir($dir_handle)) {
if($file == "." || $file == ".." || $file == "index.php" )
continue;
echo "<a href='$path/$file'>$file</a><br />"; //return the name of the track
}
// Close
closedir($dir_handle);
It's purpose is to automatically list every sound track cointained in a directory, the name of which is given by the ID passed through the URL. Each track is named with the format "### - title.mp3", e.g. "101 - Overture.mp3".
It works fine, but the resulting list is sorted randomly for some reason. Is there any way to sort the tracks by title? Also, I'm pretty much a newbie with PHP, is there any security issue with the GET function? Thanks in advance.
EDIT: The GET is only used to specify the path, it's not supposed to interact with the database. Is this enough to prevent attacks?
$ost = $_GET['id'];
$bad = array("../","=","<", ">", "/","\"","`","~","'","$","%","#");
$ost = str_replace($bad, "", $ost);
$path = "audio/soundtracks/$ost";

Do some checks on GET parameter before using it. Like checking it is numeric, right lenght etc. And msyql_real_escape_String if used against db.
When looping directory, save files in array in php, with title as index. like this, then you can sort it as you please:
while ($file = readdir($dir_handle)) {
if($file == "." || $file == ".." || $file == "index.php" )
continue;
$array[$file] = "<a href='$path/$file'>$file</a><br />"; //return the name of the track
}
sort($array);
... after this, loop and print array separately.
It is a better coding practice to first loop to arrays, and then print separatly... in my eyes. It is more flexible.

Apart from checking length and using escape string security measures on the $_GET, you can also encode and decode the id into the URL and decode the before using it.
//before putting into URL
$id = $rows["id"];
$id = base64_encode($id);
<a href="yourUrl.php?id='$id'"
//in yourUrl.php
$id = $_GET['id'];
$id = base64_decode($id);

Related

Safety on 404 error doc

I am working on my 404 error doc, and I was thinking instead of just giving a sitemap, one could suggest to the user the website he might have looked for based on what actually exists on the server.
Example: if the person typed in "www.example.com/foldr/site.html", the 404 page could output:
Did you mean "www.example.com/folder/site.html"?
For this, I wrote the following code which works for me very well. My question now is: is it "safe" to use this? As basically someone could detect all files on the server by trying all kind of combinations. Or a hacker could even use a script that loops through and lists all types of valid URLs.
Should I limit the directories this script can detect and propose? With an array of "OK"-locations, or by file type?
Had anyone else already got an idea like this?
PHP:
// get incorrect URL that was entered
$script = explode("/",$_SERVER['SCRIPT_NAME']);
$query = $_SERVER['QUERY_STRING'];
// create vars
$match = array();
$matched = "../";
// loop through the given URL folder by folder to find the suggested location
foreach ($script as $dir) {
if (!$dir) {
continue;
}
if ($handle = opendir($matched)) {
while (false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != "..") {
similar_text($dir, $entry, $perc);
if ($perc > 80) {
$match[$entry] = $perc;
}
}
}
closedir($handle);
if ($match) {
arsort($match);
reset($match);
$matched .= key($match)."/";
} else {
$matched = false;
break;
}
$match = array();
}
}
// trim and echo the result that had the highest match
$matched = trim(ltrim(rtrim($matched,"/"),"."));
echo "proposed URL: ".$_SERVER["SERVER_NAME"].$matched;
Yup, you can see it as this:
Imagine a house with only glass walls on the outside, but it's night. You're a thief (hacker) and you want to check the house for worthfull loot (files with passwords, db connections etc).
If you don't protect (certain) files, you would be putting the lights on in every part of the house. The thief would look through the windows and see that you have loot - now the only the he would have to do is get in and take it.
If you do protect the files, the thief won't even be able to know that there was any loot in the house, and thus would the thief have a higher chance of moving on to the next house.

When using readdir empty strings returned

I am using readdir in the following code to get a list of all file names of images in a directory.
while (false !== ($entry = readdir($frameDir))){
$shapeName = explode('.',$entry);
if (!empty($shapeName[0]) && $shapeName[0] != '.' && $shapeName[0] != '..' && $shapeName[0] != '/'){
$shapeName = $shapeName[0];
$shapes['frames'][] = $shapeName;
}
After this code the script appends the '.png' to make it a valid file name.
As you can see I've tried to eliminate any chances of a blank file name being passed. Though when I run the script I end up getting a blank directory "/shapes/frame/.png" . This only happens for this particular directory. When I use the code on another of the three directories I get results as expected, and the code is the same logic as what is used above.
while (false !== ($entry = readdir($frameDotDir))){
$shapeName = explode('.',$entry);
if (!empty($shapeName[0]) && $shapeName[0] != '.' && $shapeName[0] != '..' && $shapeName[0] != '/'){
$shapeName = $shapeName[0];
$shapes['frame_dots'][] = $entry;
}
}
When checking the filesystem on the server, I can't find any files with blank names.
I am wondering what could be causing my script to be reading blank file names from the diretory.
File names cannot be empty (and will not). You did something wrong in your code. It should look like:
while ($entry = readdir($frameDir)){
// skip files which names starting with a dot
// like '.', '..' or hidden files
if (strpos($entry, '.') !== 0) {
$shapes['frame_dots'][] = $entry;
}
}
You see, less is more ;)
Why don't you use glob() instead of readdir().Just give it a pattern and it will let you process the filenames with much ease instead of doing the one by one scanning work. And in your case, there is no way it will return an empty file name. Also, have a look on glob flags in the documentation, you will be amazed of it's simplicity.
glob("*.png");
Output:
Array ( [0] => shape.png, [1] => shape2.png )
I was having the same problem with blank filenames usind readdir() it turn out to be that the directory name was wrong, turns out linux is case sensitive, in code the directory name starts with "I" and in linux the directory started with "i".
I guess the error was due to not handling opendir() errors. Check your code.

Ordering output when using readdir()

I'm fairly new to PHP and have been using PHP's readdir() to look into a folder full of images and render them out dynamically based on how many images there are in that folder. Everything works great, but one thing I've noticed is that the images are not displayed in the order that they appear on my local machine HD.
So my question to anyone who knows PHP is, is there way of using PHP to read the contents of a folder AND display them in order without having to rename the actual file names e.g. 01.jpg, 02.jpg etc etc?
Have a look at the glob() function, it returns files alphabetically sorted by default:
$files = glob('/some/path/*.*');
Bonus, you can filter just images, and leave out directories.
readdir likely just takes the file system order. Which is alphabetical on NTFS, but seemingly random on most Unix filesystems. The documentation even says as much: »The entries are returned in the order in which they are stored by the filesystem.«
So you'd have to store the list in an array and sort that based on how you would like them to be sorted.
The php manual says:
string readdir ([ resource $dir_handle ] )
Returns the name of the next entry in the directory. The entries are returned in the order in which they are stored by the filesystem.
Meaning they should appear the same way.
More information found in the manual.
Why not apply one of the sort-functions of PHP?
$files = readdir( $theFoldersPath );
sort( $files );
Here is what I came up with in answer (together with the help of the people who posted) to my own question.
<?php
$dir = "low res";
$returnstr = "";
// The first part puts all the images into an array, which I can then sort using natsort()
$images = array();
if ($handle = opendir($dir)) {
while ( false !== ($entry = readdir($handle))) {
if ($entry != "." && $entry != ".."){
$images[] = $entry;
}
}
closedir($handle);
}
natsort($images);
print_r($images);
$newArray = array_values($images);
// This bit then outputs all the images in the folder along with it's own name
foreach ($newArray as $key => $value) {
// echo "$key - <strong>$value</strong> <br />";
$returnstr .= '<div class="imgWrapper">';
$returnstr .= '<div class="imgFrame"><img src="'. $dir . '/' . $value . '"/></div>';
$returnstr .= '<div class="imgName">' . $value . '</div>';
$returnstr .= '</div>';
}
echo $returnstr;
?>

PHP readir results - trying to sort by date created and also get rid of "." and ".."

I have a double question. Part one: I've pulled a nice list of pdf files from a directory and have appended a file called download.php to the "href" link so the pdf files don't try to open as a web page (they do save/save as instead). Trouble is I need to order the pdf files/links by date created. I've tried lots of variations but nothing seems to work! Script below. I'd also like to get rid of the "." and ".." directory dots! Any ideas on how to achieve all of that. Individually, these problems have been solved before, but not with my appended download.php scenario :)
<?php
$dir="../uploads2"; // Directory where files are stored
if ($dir_list = opendir($dir))
{
while(($filename = readdir($dir_list)) !== false)
{
?>
<p><a href="http://www.duncton.org/download.php?file=login/uploads2/<?php echo $filename; ?>"><?php echo $filename;
?></a></p>
<?php
}
closedir($dir_list);
}
?>
While you can filter them out*, the . and .. handles always come first. So you could just cut them away. In particular if you use the simpler scandir() method:
foreach (array_slice(scandir($dir), 2) as $filename) {
One could also use glob("dir/*") which skips dotfiles implicitly. As it returns the full path sorting by ctime then becomes easier as well:
$files = glob("dir/*");
// make filename->ctime mapping
$files = array_combine($files, array_map("filectime", $files));
// sorts filename list
arsort($files);
$files = array_keys($files);

PHP/Javascript/Jquery - Dynamic Website Diagram

I want to have an application that displays all of my website's external links and outputs a diagram. Like for example www.example.com/articles/some-title.html is linked to my homepage.
Home
- www.example.com/some-text
- www.another-site.com/my-title
- www.example.com/articles/some-title.html Products
Products
- www.buy-now.com/product-reviews/231/098989
- www.sales.com/432/title-page.html Categories
- www.ezinearticles.com/blah-blah-blah
Something like SlickMap, but not on CSS.
I have setup a table on my DB so this will be dynamic and more links to come. I'm using CakePHP in working on this. Any ideas/suggestions?
Thanks for your time.
You can see slickmap, is a css implementation for site diagrams
http://astuteo.com/slickmap/
You can use PHP to retrieve the results from the database and you can use jQuery's treeView to display them.
Also, raphaël.js might be of interest, especially its diagram plugin, its fully customizable and should be something to check out.
If I am understanding you correctly, you want to parse the contents of an entire web site (HTML, JS, etc...), and create an array that contains all of your links, as well as the pages that they can be found on. If that is correct, this code will get the job done:
<?php
$path = "./path_to_your_files/";
$result = array();
if ( $handle = opendir($path) ) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != "..") {
$contents = file_get_contents($path . $file);
preg_match_all("/a[\s]+[^>]*?href[\s]?=[\s\"\']+"."(.*?)[\"\']+.*?>"."([^<]+|.*?)?<\/a>/", $contents, $parts);
foreach ( $parts[1] as $link ) {
$result[$file][] = $link;
}
}
}
closedir($handle);
}
print_r($result);
?>

Categories