I've changed the function below to support the 4xx errors and the 30x redirects... This is a partial implementation yet but it's sufficient for the normal usage.
I've made a recursive implementation (if a 30x redirect is found), but it could be easily reverted to an iterative way (simple put something like while (!empty $url) at the beginning, and set the $url to an empty string if no 3xx/4xx status are found).
<?
function http_get($url, $range = 0)
{
$url_stuff = parse_url($url);
$port = isset($url_stuff['port']) ? $url_stuff['port'] : 80;
$fp = @fsockopen($url_stuff['host'], $port);
if (!$fp)
return false;
$query = 'GET '.$url_stuff['path'].'?'.$url_stuff['query']." HTTP/1.1\r\n";
$query .= 'Host: '.$url_stuff['host']."\r\n";
$query .= 'Connection: close'."\r\n";
$query .= 'Cache-Control: no'."\r\n";
$query .= 'Accept-Ranges: bytes'."\r\n";
if ($range != 0)
$query .= 'Range: bytes='.$range.'-'."\r\n"; // -500
//$query .= 'Referer: http:/...'."\r\n";
//$query .= 'User-Agent: myphp'."\r\n";
$query .= "\r\n";
fwrite($fp, $query);
$chunksize = 1*(1024*1024);
$headersfound = false;
while (!feof($fp) && !$headersfound) {
$buffer .= @fread($fp, 1);
if (preg_match('/HTTP\/[0-9]\.[0-9][ ]+([0-9]{3}).*\r\n/', $buffer, $matches)) {
$headers['HTTP'] = $matches[1];
$buffer = '';
} else if (preg_match('/([^:][A-Za-z_-]+):[ ]+(.*)\r\n/', $buffer, $matches)) {
$headers[$matches[1]] = $matches[2];
$buffer = '';
} else if (preg_match('/^\r\n/', $buffer)) {
$headersfound = true;
$buffer = '';
}
if (strlen($buffer) >= $chunksize)
return false;
}
if (preg_match('/4[0-9]{2}/', $headers['HTTP']))
return false;
else if (preg_match('/3[0-9]{2}/', $headers['HTTP']) && !empty($headers['Location'])) {
$url = $headers['Location'];
return http_get($url, $range);
}
while (!feof($fp) && $headersfound) {
$buffer = @fread($fp, $chunksize);
echo $buffer;
ob_flush();
flush();
}
$status = fclose($fp);
return $status;
}
?>
Aπομακρυσμένα αρχεία
Όσο το allow_url_fopen είναι ενεργοποιημήνο στο php.ini, μπορείτε να χρησιμοποιείτε τα HTTP και FTP URL με τις περισσότερες συναρτήσεις που παίρνουν ένα όνομα αρχείου σαν παράμετρο. Επιπλέον, τα URL μπορούν να χρησιμοποιηθούν με τις include(), include_once(), require() και require_once() δηλώσεις. Δείτε το List of Supported Protocols/Wrappers για περισσότερες πληροφορίες σχετικά με τα πρωτόκολλα που υποστηρίζονται από την PHP.
Note: Στην PHP 4.0.3 και παλαιότερα, για να χρησιμοποιήσετε ταURL wrappers, χρειαζόταν να κάνετε configure τη PHP χρησιμοποιώντας την επιλογή του configure --enable-url-fopen-wrapper.
Note: Οι Windows εκδόσεις της PHP πριν την PHP 4.3 δεν υποστήριζαν πρόσβαση απομακρυσμένων αρχείων για τις ακόλουθες συναρτήσεις: include(), include_once(), require(), require_once(), και τις imagecreatefromXXX συναρτήσεις στην Image επέκταση.
Για παράδειγμα, μπορείτε να χρησιμοποιήσετε το εξής για να ανοίξετε ένα αρχείο σε ένα remote web server, να διαβάσετε τα δεδομένα που θέλετε, και μετά να χρησιμοποιήσετε τα δεδομένα σε μια ερώτηση βάσης δεδομένων, ή απλά να κάνετε output σε ένα στυλ που ταιριάζει την υπόλοιπη ιστοσελίδα σας.
Example#1 Παίρνοντας τον τίτλο μιας απομακρυσμένης σελίδας
<?php
$file = fopen ("http://www.example.com/", "r");
if (!$file) {
echo "<p>Unable to open remote file.\n";
exit;
}
while (!feof ($file)) {
$line = fgets ($file, 1024);
/* This only works if the title and its tags are on one line */
if (eregi ("<title>(.*)</title>", $line, $out)) {
$title = $out[1];
break;
}
}
fclose($file);
?>
Μπορείτε επίσης να γράψετε αρχεία σε ένα FTP server (δεδομένου του ότι έχετε συνδεθεί σαν ένας χρήστης με τα σωστά δικαιώματα πρόσβασης). Μπορείτε μόνο να δημιουργήσετε νέα αρχεία με αυτή τη μέθοδο, αν προσπαθήσετε να κάνετε overwrite ένα αρχείο που υπάρχει ήδη, η κλήση της fopen() θα αποτύχει.
Για να συνδεθείτε σαν ένας χρήστης άλλος από τον 'anonymous', πρέπει να ορίσετε το username (και πιθανώς το password) μέσα στο URL, κάπως έτσι: 'ftp://user:password@ftp.example.com/path/to/file'. (Μπορείτε να χρησιμοποιήσετε την ίδια μορφή σύνταξης για να προσπελάσετε αρχεία μέσω του HTTP όταν απαιτούν βασική αναγνώριση.)
Example#2 Αποθήκευση δεδομένων σε ένα απομακρυσμένο server
<?php
$file = fopen ("ftp://ftp.example.com/incoming/outputfile", "w");
if (!$file) {
echo "<p>Unable to open remote file for writing.\n";
exit;
}
/* Write the data here. */
fputs ($file, $_SERVER['HTTP_USER_AGENT'] . "\n");
fclose ($file);
?>
Note: Μπορεί να πήρατε την ιδέα από το παραπάνω παράδειγμα πως μπορείτε να χρησιμοποιήσετε αυτή την τεχνική για να γράψετε σε ένα απομακρυσμένο logfile. Δυστυχώς αυτό δεν θα δουλέψει επειδή η κλήση της fopen() θα αποτύχει αν το απομακρυσμένο αρχείο υπάρχει ήδη. Για να κάνετε κατανεμημένο logging έτσι, πρέπει να ρίξετε μια ματιά στην syslog().
Aπομακρυσμένα αρχεία
28-Apr-2008 06:18
06-May-2006 03:53
Really, you should not send headers terminated by \n - it's not per-rfc supported by a HTTP server.
Instead, send as \r\n which is what the protocol specifies, and that regular expression would be matched anywhere, so match for something like /^Content-Length: \d+$/i on each header-line (headers are terminated by the regular expression /(\r\n|[\r\n])/ - so preg_split on that. Remeber to use the appropriate flags, I can't be arsed to look them up)
14-Sep-2004 12:06
The previous post is part right, part wrong. It's part right because it's true that the php script will run on the remote server, if it's capable of interpreting php scripts. You can see this by creating this script on a remote machine:
<?php
echo system("hostname");
?>
Then include that in a php file on your local machine. When you view it in a browser, you'll see the hostname of the remote machine.
However, that does not mean there are no security worries here. Just try replacing the previous script with this one:
<?php
echo "<?php system(\"hostname\"); ?>";
?>
I'm guessing you can figure out what that's gonna do.
So yes, remote includes can be a major security problem.
04-Aug-2003 05:25
ok, here is the story:
I was trying to download remote images, finding urls throught apache indexs with regexps and fopen()ing them to get the datas. It didn't work. I thought about binary considerations. Putting the 'b' in the second argument of fopen didn't help much, my browser still didn't want to display the images. I finally understood by watching the datas i was getting from the remote host: it was an html page ! hey, i didn't know apache sent html pages when requesting images, did you ?
the right way is then to send an http request via fsockopen. Here comes my second problem, using explode("\n\n", $buffer); to get rid of the headers. The right way is to get the value of the Content-Lenght field and use it in substr($buffer, -$Content-Lenght);
finally, here is my own function to download these files:
<?php
function http_get($url)
{
$url_stuff = parse_url($url);
$port = isset($url_stuff['port']) ? $url_stuff['port'] : 80;
$fp = fsockopen($url_stuff['host'], $port);
$query = 'GET ' . $url_stuff['path'] . " HTTP/1.0\n";
$query .= 'Host: ' . $url_stuff['host'];
$query .= "\n\n";
fwrite($fp, $query);
while ($tmp = fread($fp, 1024))
{
$buffer .= $tmp;
}
preg_match('/Content-Length: ([0-9]+)/', $buffer, $parts);
return substr($buffer, - $parts[1]);
?>
}
ho, maybe you'll say i could have parsed the page to get rid of the html stuff, but i wanted to experience http a little ;)
