Now the world would not be the world we know if everyone
followed the specs. As written on
freedesktop.org,
there are plenty of implementations, and most cook their own soup.
Let it be the \r\n at the end of the line; most
programs do that right but some just forget the
\r. Some forget the terminating characters on the
last line. That is easy to catch when implementing the routine in
php.
The real problem are the URIs, or what the programmers think URIs are.
URIs of some programs
Remember the correct URI: file://host/path/to/file
KDE programs like Konqueror,
Kate and so pass the following:
file:/path/to/file. Only one "/", totally
incorrect.
Nautilus, gnome's file manager, passes
file:///path/to/file. Correct protocol
(file://), hostname left out (not really correct,
but nearly), and the full path with a leading "/".
The Opera browser gives you a
file://path/to/file. What is that? A relative
filename?
When dragging links from Firefox, you
get the same as with Nautilus:
file:///path/to/file, nearly correct.
The crux: Firefox doesn't drop files to targets accepting
text/uri-list, only to
text/plain ones.
The Windows' explorer is nearly correct
like Nautilus and Firefox, giving a
file:///c:/path/to/file.
And beside files, there can be real URLs like
http://gtk.php.net in the list.
What does that tell us? We will have a hard time finding out
where the file really is.
We need a solution which accepts URIs that follow the standard AND
is able to guess what files the source applications mean if the
URI does not conform to the standard. The resulting code seems
to be awkward, but that's the price for compatibility:
Example 4.5. Convert a (misformed) URI to a local path
/**
* converts a file path gotten from a text/uri-list
* drop to a usable local filepath
* @param string The line from the uri-list
* @return string The usable local filepath
*/
function getPathFromUrilistEntry( $strUriFile)
{
$strUriFile = urldecode($strUriFile);//should be URL-encoded
$bUrl = false;
if (substr($strUriFile, 0, 5) == 'file:')
{ //(maybe buggy) file protocol
if (substr($strUriFile, 0, 17) == 'file://localhost/') {
//correct implementation
$strFile = substr($strUriFile, 16);
} else if (substr($strUriFile, 0, 8) == 'file:///') {
//no hostname, but three slashes - nearly correct
$strFile = substr($strUriFile, 7);
} else if ($strUriFile[5] == '/') {
//theoretically, the hostname should be the first
//but no one implements it
$strUriFile = substr($strUriFile, 5);
for( $n = 1; $n < 5; $n++) {
if ($strUriFile[$n] != '/') { break; }
}
$strUriFile = substr($strUriFile, $n - 1);
if (!file_exists($strUriFile)) {
//perhaps a correct implementation with hostname???
$strUriFileNoHost = strstr(substr($strUriFile, 1), '/');
if (file_exists($strUriFileNoHost)) {
//seems so
$strUriFile = $strUriFileNoHost;
}
}
$strFile = $strUriFile;
} else {
//NO slash after "file:" - what is that for a crappy program?
$strFile = substr ($strUriFile, 5);
}
} else if (strstr($strUriFile, '://')) {
//real protocol, but not file
$strFile = $strUriFile;
$bUrl = true;
} else {
//local file?
$strFile = $strUriFile;
}
if (!$bUrl && $strFile[2] == ':' && $strFile[0] == '/') {
//windows file path
$strFile = str_replace('/', '\\', substr($strFile, 1));
}
return $strFile;
}
Now that we have a nice conversion routine, we can extend our source
to display the local path in the file tree:
Example 4.6. Displaying the local file path in the tree