');
}
$requested_flower_dname = $_GET['flower_dname'];
/*
* Only allow directories listed in flower_dirs.txt.
*
* This prevents directory traversal such as:
* ../../some-directory
* ../../../etc
*
* It also means the URL cannot select an arbitrary directory.
*/
$allowed_dirs = [];
$dir_handle = fopen('flower_dirs.txt', 'r');
if ($dir_handle === false) {
die('Could not open flower directory list
');
}
while (fscanf($dir_handle, '%s', $dir_name)) {
$allowed_dirs[] = $dir_name;
}
fclose($dir_handle);
if (!in_array($requested_flower_dname, $allowed_dirs, true)) {
die('Invalid flower directory
');
}
$flower_dname = $requested_flower_dname;
/*
* Read the flower name.
*/
$handle = fopen($flower_dname . '/name.txt', 'r');
if ($handle === false) {
die('Could not open flower name file
');
}
$flower_name = trim(fread($handle, 256));
fclose($handle);
/*
* Escape the flower name whenever it is placed into HTML.
*/
$safe_flower_name = htmlspecialchars(
$flower_name,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
/*
* Read the main text.
*/
$handle = fopen($flower_dname . '/main_text.txt', 'r');
if ($handle === false) {
die('Could not open main text file
');
}
$main_text = fread($handle, 100000);
fclose($handle);
/*
* Read and parse locations.txt if it exists.
*/
$location_array = [];
$location_file = $flower_dname . '/locations.txt';
if (file_exists($location_file)) {
$handle = fopen($location_file, 'r');
if ($handle === false) {
die('Could not open locations.txt
');
}
$location_text = fread($handle, 100000);
fclose($handle);
/*
* Parse:
*
* [place name] {description}
*/
$count = 0;
while (
preg_match(
'/\[([^\]]*)\]/',
$location_text,
$captured
) &&
$count < 1000
) {
$place_name = $captured[1];
$location_text = strstr(
$location_text,
'[' . $place_name . ']'
);
if (
preg_match(
'/\{([^}]*)\}/',
$location_text,
$captured
)
) {
$description = $captured[1];
$location_text = strstr(
$location_text,
'{' . $description . '}'
);
$location_array[$place_name] = $description;
}
$count++;
}
}
/*
* Read links.txt.
*/
$handle = fopen('links.txt', 'r');
if ($handle === false) {
die('Could not open links.txt
');
}
$link_text = fread($handle, 100000);
fclose($handle);
/*
* Parse:
*
* [key text] {url}
*/
$link_array = [];
$count = 0;
while (
preg_match(
'/\[([^\]]*)\]/',
$link_text,
$captured
) &&
$count < 1000
) {
$link_key = $captured[1];
$link_text = strstr(
$link_text,
'[' . $link_key . ']'
);
if (
preg_match(
'/\{([^}]*)\}/',
$link_text,
$captured
)
) {
$link_url = trim($captured[1]);
$link_text = strstr(
$link_text,
'{' . $link_url . '}'
);
/*
* Only permit normal web URLs.
*
* In particular, reject:
* javascript:
* data:
* vbscript:
*
* and other dangerous schemes.
*/
$parsed_url = parse_url($link_url);
if (
$parsed_url !== false &&
isset($parsed_url['scheme']) &&
in_array(
strtolower($parsed_url['scheme']),
['http', 'https'],
true
)
) {
$link_array[strtolower($link_key)] = $link_url;
}
}
$count++;
}
/*
* Expand [[link keys]] in main_text.
*
* The link text is escaped.
* The URL is escaped separately for the HTML attribute.
*/
while (
preg_match(
'/\[\[([[:alnum:][:space:]_.\/,\-]*)\]\]/',
$main_text,
$captured
)
) {
$original_tag = $captured[0];
$link_key = strtolower($captured[1]);
if (array_key_exists($link_key, $link_array)) {
$safe_link_text = htmlspecialchars(
$captured[1],
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
$safe_link_url = htmlspecialchars(
$link_array[$link_key],
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
$replacement =
'' .
$safe_link_text .
'';
$main_text = str_replace(
$original_tag,
$replacement,
$main_text
);
} else {
$safe_link_text = htmlspecialchars(
$captured[1],
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
$main_text = str_replace(
$original_tag,
$safe_link_text . ' UNMATCHED',
$main_text
);
}
}
/*
* Main image.
*
* The filename is generated from the validated flower directory,
* so encode it when putting it into HTML.
*/
$main_image_fname =
$flower_dname . '/main_image.jpg';
$safe_main_image_fname = htmlspecialchars(
$main_image_fname,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
?>
');
}
$i = 0;
while (fscanf($handle, '%s', $cur_thumb_fname)) {
/*
* Only accept a filename, not a path.
*
* This prevents contents.txt from introducing:
* ../
* directory/path
*/
if (
basename($cur_thumb_fname) === $cur_thumb_fname &&
$cur_thumb_fname !== ''
) {
$thumb_fname[$i++] =
$flower_dname .
'/images_thumb/' .
$cur_thumb_fname;
}
}
fclose($handle);
/*
* Read large/display filenames.
*/
$display_fname = [];
$handle = fopen(
$flower_dname . '/images_large/contents.txt',
'r'
);
if ($handle === false) {
die('Could not open large image contents file
');
}
$i = 0;
while (fscanf($handle, '%s', $cur_display_fname)) {
if (
basename($cur_display_fname) === $cur_display_fname &&
$cur_display_fname !== ''
) {
$display_fname[$i++] =
$flower_dname .
'/images_large/' .
$cur_display_fname;
}
}
fclose($handle);
/*
* Generate thumbnail block.
*/
echo '';
foreach ($thumb_fname as $i => $cur_thumb_fname) {
/*
* Make sure a corresponding large image exists.
*/
if (!isset($display_fname[$i])) {
continue;
}
$cur_display_fname = $display_fname[$i];
/*
* Encode URL parameters.
*/
$image_url =
'display_image.php?image_fname=' .
rawurlencode($cur_display_fname) .
'&image_title=' .
rawurlencode($flower_name);
/*
* Escape the complete URL because it is going into href.
*/
$safe_image_url = htmlspecialchars(
$image_url,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
$safe_thumb_fname = htmlspecialchars(
$cur_thumb_fname,
ENT_QUOTES | ENT_SUBSTITUTE,
'UTF-8'
);
echo '';
echo '
';
echo '';
}
echo '
';
?>
Copyright © 2009, 2012, Randal C. Nelson