Bittorrent macht mittlerweile einen Großteil des weltweiten Datenverkehrs aus. Ohne eine Torrentdatei und einen dazugehörigen Tracker läuft hier nichts. Ich habe eine Klasse in PHP erstellt, welche die Daten aus einer Torrentdatei auslesen kann und eine Anfrage an den Tracker schickt, welcher schließlich die IP-Adressen ausspuckt.
Es läßt sich mit dieser Klassen ein kompletter Client gegenüber dem Tracker simulieren, vom Starten des Downloads bis zum fertigstellen.
Dateien werden bei diesem Vorgang nicht übertragen (wird wohl in PHP nicht machbar sein). Es soll hier nur um das Verständnis der Kommunikation zwischen Client und Tracker über das http-Protokoll und des Bencoding gehen.
Torrent-Information
Als Beispiel Torrent-Datei hab ich mir ein Debian Image genommen
Zeigt den Torrentname (in der Torrent Dateien, nicht den Dateinamen) und die aufgeführten Dateien in der Torrentdatei
$client= new TorrentClient("Torrentflux","debian-500-i386-DVD-1.iso.torrent"); echo $client->getTorrentName()."\r\n"; print_r($client->getTorrentFiles());
debian-500-i386-DVD-1.iso
Array
(
[debian-500-i386-DVD-1.iso] => Array
(
[size] => 4698322944
)
)Weitere Torrent-Informationen stehen zur Verfügung z.B Hashwert, größe des gesamten Downloads und Trackerurl
$client= new TorrentClient("Torrentflux","debian-500-i386-DVD-1.iso.torrent"); echo "AnnounceUrl:".$client->getTorrentAnnounceUrl()."\r\n"; echo "Hash:".$client->getTorrentHash()."\r\n"; echo "Size:".$client->getTorrentSize()."\r\n";
AnnounceUrl:http://bttracker.debian.org:6969/announce Hash:cf92138268871bc3bb964ed69db1ec36c2ce9759 Size:4698322944
Tracker response
Möchte man eine Trackeranfrage senden so geht das über announcing. getAnnoucedPeers stellt schließlich als Array die Peers über IP,Port und PeerID (soweit vorhanden) zur Verfügung. Da viele Tracker aus Performence gründen nur max. 50 Peers zur Verfügung stellen, gibt meist in der Antwort noch einen extra Wert für die gesamt Anzahl der Peers. Diesen Wert bekommt man über getAnnoucedPeersCount.
Bei dieser Anfrage wird dem Tracker ein Start-Event geschickt, wie es jeder Client machen würde.
$client= new TorrentClient("Torrentflux","debian-500-i386-DVD-1.iso.torrent"); $client->announcing(); echo "Peers on server:\r\n"; echo $client->getAnnoucedPeersCount()."\r\n"; echo "Peers to connect:\r\n"; print_r($client->getAnnoucedPeers());
Peers on server:
49
Peers to connect:
Array
(
[0] => Array
(
[ip] => 68.231.xxx.xxx
[port] => 9798
)
[1] => Array
(
[ip] => 89.248.xxx.xxx
[port] => 12730
)
...
)Neben dem Start Event gibt es weiterhin noch update, stopped und complete. Hier müssen allerdings weitere Angaben übernommen werden. Daten wie PeerID,PeerKey,Client (Beispiel $ar) müssen der Anfrage aus dem Start-Event entsprechen. Eine Übergabe der Torrentdatei ist nicht erforderlich sichert man sich vorher die entsprechenden Daten.
$a= new TorrentClient($ar["Client"]); $a->peer_id=$ar['PeerID']; $a->peer_key=$ar['PeerKey']; $a->an_event="update"; $a->an_download=$ar['DownloadedBytes']; $a->an_upload=$ar['UploadedBytes']; $a->an_left=($ar['LeftBytes'] - $ar['DownloadedBytes']); $an=$a->announcing($ar["Hash"],$ar["AnnouceURL"]);
/trunk/bittorrent_announce/bittorrent_announce_class.php
<?php // $Id: bittorrent_announce_class.php 10 2010-08-15 14:25:26Z espendiller $ /* Version 0.1 public more on http://www.espend.de - PHP BitTorrent Announce Client */ class TorrentClient { private $peer_header; private $peer_url; var $peer_id; var $peer_key; private $torrent_bdecode; private $torrent_raw; private $request; var $request_proxy=""; var $request_php_proxy=""; var $an_port=3366; var $an_event="started"; var $an_upload=0; var $an_download=0; var $an_left=0; var $an_numpeers=50; function TorrentClient($client="Info",$torrentfile="") { switch ($client) { case 'Transmission': $this->peer_url="%host%info_hash=%hash%&peer_id=-TR0006-%peer%&port=%port%&uploaded=%upload%&downloaded=%download%&left=%left%&compact=1&numwant=%numpeers%&key=%key%%event%"; $this->peer_header=array("User-Agent: Transmission/0.6","Content-length: 0","Connection: close"); $this->peer_id=$this->getpass(12,0); $this->peer_key=$this->getpass(20,0); break; case 'Torrentflux': $this->peer_url="%host%info_hash=%hash%&peer_id=T03I-----%peer%&port=%port%&uploaded=%upload%&downloaded=%download%&left=%left%&no_peer_id=1&compact=1%event%&key=%key%"; $this->peer_header=array("User-Agent: BitTornado/T-0.3.18","Accept-Encoding: gzip"); $this->peer_id=$this->getpass(11,1); $this->peer_key=$this->getpass(6,1); break; case 'Info': break; default: echo "TorrentClient error: Client ".$client." unknown"; exit; } if ($torrentfile!="") { if (is_file($torrentfile)) $torrentfile=$this->d_read($torrentfile); $this->torrent_bdecode=$this->BDecode($torrentfile); $this->torrent_raw=$torrentfile; $this->an_left=$this->getTorrentSize(); } } private function BDecode($wholefile) { $decoder = new BDecode; $return = $decoder->decodeEntry($wholefile); return $return[0]; } function announcing($hash="",$announce="",$returnOnlyUrl=false) { if (is_array($this->torrent_bdecode)==false) { if ($hash=="" AND $announce=="") { echo "TorrentClient error: not enough announce information"; exit; return ""; } } $url = $this->peer_url; if ($hash=="") $hash=$this->getTorrentHash(); if ($announce=="") $announce=$this->getTorrentAnnounceUrl(); if (strpos($announce,"?") > 0 ) { $announce.="&"; } else { $announce.="?"; } $url = str_replace("%hash%",urlencode(pack("H*", $hash)),$url); $url = str_replace("%host%",$announce,$url); if ($this->an_event=="update") { $url = str_replace("%event%","",$url); } else { $url = str_replace("%event%","&event=".$this->an_event,$url); } $url = str_replace("%download%",$this->an_download,$url); $url = str_replace("%upload%",$this->an_upload,$url); $url = str_replace("%left%",$this->an_left,$url); $url = str_replace("%port%",$this->an_port,$url); $url = str_replace("%peer%",$this->peer_id,$url); $url = str_replace("%key%",$this->peer_key,$url); $url = str_replace("%numpeers%",$this->an_numpeers,$url); d_upd("debug.txt",date("H:i:s")." - ".$url."\r\n"); if ($returnOnlyUrl==true) return $url; return $this->performRequest($url); } private function unesc($x) { if (get_magic_quotes_gpc()==false) return stripslashes($x); return $x; } private function fetchURL($url) { $url_parsed = parse_url($url); $host = $url_parsed["host"]; $port = $url_parsed["port"]; if ($port==0) $port = 80; $path = $url_parsed["path"]; if ($url_parsed["query"] != "") $path .= "?".$url_parsed["query"]; if($this->request_proxy!="") { $pro=split(":",$this->request_proxy); $fp = fsockopen($pro[0], $pro[1], $errno, $errstr, 30); $out .= 'GET http://'.$host.":".$port.$path.' HTTP/1.0'."\r\n"; } else { $fp = fsockopen($host, $port, $errno, $errstr, 30); $out .= 'GET '.$path.' HTTP/1.0'."\r\n"; } $out .= 'Host: '.$host."\r\n"; $out.=implode("\r\n", $this->peer_header); $out.="\r\n\r\n"; fwrite($fp, $out); $body = false; while (!feof($fp)) { $s = fgets($fp, 1024); if($body==true) { $in .= $s; } else { $b.=$s; } if ( $s == "\r\n" ) $body = true; } fclose($fp); if (strpos($b,"deflate") > 0) $in = gzuncompress($in); if (strpos($b,"gzip") > 0) $in = gzinflate(substr($in,10)); return $in; } private function performRequest($url) { $url1=$url; if ($this->request_php_proxy!="") $url1=$this->request_php_proxy."?p=".bin2hex($url); $get = $this->fetchURL($url1); if (strpos($get,"failure")>0) return "error:".$get; if (!strlen($get)>0 OR strpos($get,"404 - ")>0 ) return "error:keine/falsche antwort"; # $get=$this->unesc($get); $this->request=$this->bdecode($get); return $get; #exit; $browser = new browser(); $browser->set_extra_headers($this->peer_header); if ($this->request_proxy!="") $browser->proxy=$this->request_proxy; $site=$browser->site($url); if ($this->request_php_proxy!="") $site=$browser->site($this->request_php_proxy."?p=".bin2hex($url)); $site->get(); $get=$this->unesc($site->get_content()); $get_h=$site->get_headers(); if (strpos(implode("\r\n", $get_h),"deflate") > 0) $get = gzuncompress($get); if (strpos(implode("\r\n", $get_h),"gzip") > 0) $get = gzinflate(substr($get,10)); if (strpos($get,"failure")>0) return "error:".$get; if (!strlen($get)>0 OR strpos($get,"404 - ")>0 ) return "error:keine/falsche antwort"; $this->request=$this->bdecode($get); return $get; } function performRequestOverride($datei) { $str=$this->d_read($datei); $this->request=$this->bdecode($str); } function getTorrentFiles() { return $this->torFiles($this->torrent_bdecode); } function getTorrentSize() { return $this->torSize($this->torrent_bdecode); } function getPeerKey() { return $this->peer_key; } function getPeerID() { return $this->peer_id; } function getTorrentHash() { return sha1($this->BEncode($this->torrent_bdecode["info"])); } function replaceAnnounce($newURL) { $adresse= $this->torrent_bdecode['announce']; $this->torrent_raw=str_replace($this->BEncode($adresse),$this->BEncode($newURL),$this->torrent_raw); $this->torrent_bdecode=$this->BDecode($this->torrent_raw); return $this->torrent_raw; } function getTorrentName() { return $this->torrent_bdecode['info']['name']; } function getTorrentAnnounceUrl() { #not multitrackersuppert at the moment #[announce-list] => Array ( [0] => Array ( [0] => http://tpb.tracker.thepiratebay.org/announce return $this->torrent_bdecode["announce"]; } function getAnnoucedPeersCount() { $peers=array(); if (is_array($this->request)) { if (!$peers>0) $peers=$this->request['value']['complete']['value']+$this->request['value']['incomplete']['value']; if (!$peers>0) $peers=$this->request['complete']+$this->request['incomplete']; if (!$peers>0) $peers = count($this->getAnnoucedPeers()); } return $peers; } function getAnnoucedPeers() { #$an=$this->bdecode($this->request); $an=$this->request; if (count($an)>0) { $ips=$this->bdec_bin($an['peers']); return $ips; } else { return""; } } function getAnnoucedInterval() { return $this->request['interval']; } private function BEncode($array) { $string = ""; $encoder = new BEncode; $encoder->decideEncode($array, $string); return $string; } private function torFiles($torrentBec) { $b = array(); // torrent with single file if (isset($torrentBec['info']['length'])) { $b[$torrentBec['info']['name']] = array( 'path' => $torrentBec['info']['name'], 'size' => $torrentBec['info']['length'], ); return $b; } // torent with more than one file foreach ($torrentBec['info']['files'] as $row) { $datei=implode("\\", $row['path']); $b[$datei] = array( 'path' => implode("\\", $row['path']), 'size' => $row['length'], ); } return $b; } private function torSize($torrentBec) { if (!isset($torrentBec['info']['files'])) return $torrentBec['info']['length']; $files = $torrentBec['info']['files']; $size=0; foreach ($files as $row) { $size=$size+$row['length']; } return $size; } private function getpass($laenge,$str) { $newpass = ""; $string[0]="abcdefghijklmnopqrstuvwxyz0123456789"; $string[1]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; mt_srand((double)microtime()*1000000); for ($i=1; $i <= $laenge; $i++) { $newpass .= substr($string[$str], mt_rand(0,strlen($string[$str])-1), 1); } return $newpass; } private function d_read($datei) { if (!file_exists($datei)) return ""; $header=""; $dateizeiger=fopen($datei,"r"); while(!feof($dateizeiger)) { $header .= fgets($dateizeiger); } fclose($dateizeiger); return $header; } private function bdec_bin($peerlist) { $back[]['info']="nichts"; if (!is_array($peerlist)>0) { $back=array(); $peers=str_split($peerlist,6); foreach ($peers as $row) { $back[]=$this->bin_met($row); } } if (is_array($peerlist)) return $peerlist; return $back; } private function bin_met($packed) { $back=array(); if (strlen($packed)>0) { $back = unpack('Nip/nport', $packed); $back['ip']=long2ip($back['ip']); } return $back; } } class BDecode { function numberdecode($wholefile, $start) { $ret[0] = 0; $offset = $start; // Funky handling of negative numbers and zero $negative = false; if ($wholefile[$offset] == '-') { $negative = true; $offset++; } if ($wholefile[$offset] == '0') { $offset++; if ($negative) return array(false); if ($wholefile[$offset] == ':' || $wholefile[$offset] == 'e') { $offset++; $ret[0] = 0; $ret[1] = $offset; return $ret; } return array(false); } while (true) { if ($wholefile[$offset] >= '0' && $wholefile[$offset] <= '9') { $ret[0] *= 10; //Added 2005.02.21 - VisiGod //Changing the type of variable from integer to double to prevent a numeric overflow settype($ret[0],"double"); //Added 2005.02.21 - VisiGod $ret[0] += ord($wholefile[$offset]) - ord("0"); $offset++; } // Tolerate : or e because this is a multiuse function else if ($wholefile[$offset] == 'e' || $wholefile[$offset] == ':') { $ret[1] = $offset+1; if ($negative) { if ($ret[0] == 0) return array(false); $ret[0] = - $ret[0]; } return $ret; } else return array(false); } } function decodeEntry($wholefile, $offset=0) { if ($wholefile[$offset] == 'd') return $this->decodeDict($wholefile, $offset); if ($wholefile[$offset] == 'l') return $this->decodelist($wholefile, $offset); if ($wholefile[$offset] == "i") { $offset++; return $this->numberdecode($wholefile, $offset); } // String value: decode number, then grab substring $info = $this->numberdecode($wholefile, $offset); if ($info[0] === false) return array(false); $ret[0] = substr($wholefile, $info[1], $info[0]); $ret[1] = $info[1]+strlen($ret[0]); return $ret; } function decodeList($wholefile, $start) { $offset = $start+1; $i = 0; if ($wholefile[$start] != 'l') return array(false); $ret = array(); while (true) { if ($wholefile[$offset] == 'e') break; $value = $this->decodeEntry($wholefile, $offset); if ($value[0] === false) return array(false); $ret[$i] = $value[0]; $offset = $value[1]; $i ++; } // The empy list is an empty array. Seems fine. $final[0] = $ret; $final[1] = $offset+1; return $final; } // Tries to construct an array function decodeDict($wholefile, $start=0) { $offset = $start; if ($wholefile[$offset] == 'l') return $this->decodeList($wholefile, $start); if ($wholefile[$offset] != 'd') return false; $ret = array(); $offset++; while (true) { if ($wholefile[$offset] == 'e') { $offset++; break; } $left = $this->decodeEntry($wholefile, $offset); if (!$left[0]) return false; $offset = $left[1]; if ($wholefile[$offset] == 'd') { // Recurse $value = $this->decodedict($wholefile, $offset); if (!$value[0]) return false; $ret[addslashes($left[0])] = $value[0]; $offset= $value[1]; continue; } else if ($wholefile[$offset] == 'l') { $value = $this->decodeList($wholefile, $offset); if (!$value[0] && is_bool($value[0])) return false; $ret[addslashes($left[0])] = $value[0]; $offset = $value[1]; } else { $value = $this->decodeEntry($wholefile, $offset); if ($value[0] === false) return false; $ret[addslashes($left[0])] = $value[0]; $offset = $value[1]; } } if (empty($ret)) $final[0] = true; else $final[0] = $ret; $final[1] = $offset; return $final; } } // End of class declaration. class BEncode { // Dictionary keys must be sorted. foreach tends to iterate over the order // the array was made, so we make a new one in sorted order. :) function makeSorted($array) { $i = 0; // Shouldn't happen! if (empty($array)) return $array; foreach($array as $key => $value) $keys[$i++] = stripslashes($key); sort($keys); for ($i=0 ; isset($keys[$i]); $i++) $return[addslashes($keys[$i])] = $array[addslashes($keys[$i])]; return $return; } // Encodes strings, integers and empty dictionaries. // $unstrip is set to true when decoding dictionary keys function encodeEntry($entry, &$fd, $unstrip = false) { if (is_bool($entry)) { $fd .= "de"; return; } if (is_int($entry) || is_float($entry)) { $fd .= "i".$entry."e"; return; } if ($unstrip) $myentry = stripslashes($entry); else $myentry = $entry; $length = strlen($myentry); $fd .= $length.":".$myentry; return; } // Encodes lists function encodeList($array, &$fd) { $fd .= "l"; // The empty list is defined as array(); if (empty($array)) { $fd .= "e"; return; } for ($i = 0; isset($array[$i]); $i++) $this->decideEncode($array[$i], $fd); $fd .= "e"; } // Passes lists and dictionaries accordingly, and has encodeEntry handle // the strings and integers. function decideEncode($unknown, &$fd) { if (is_array($unknown)) { if (isset($unknown[0]) || empty($unknown)) return $this->encodeList($unknown, $fd); else return $this->encodeDict($unknown, $fd); } $this->encodeEntry($unknown, $fd); } // Encodes dictionaries function encodeDict($array, &$fd) { $fd .= "d"; if (is_bool($array)) { $fd .= "e"; return; } // NEED TO SORT! $newarray = $this->makeSorted($array); foreach($newarray as $left => $right) { $this->encodeEntry($left, $fd, true); $this->decideEncode($right, $fd); } $fd .= "e"; return; } } // End of class declaration. class browser { var $user_agent; var $accept; var $language; var $charset; var $referer; var $cookies; var $extra_headers; var $proxy; var $debug; function browser() { // defaults, can override $this->user_agent = false; $this->accept = false; $this->language = false; $this->charset = false; $this->referer = false; $this->cookies = false; $this->extra_headers = false; $this->proxy = false; $this->debug = false; } // set user-agent // $user_agent as string function set_user_agent($user_agent = false) { $this->user_agent = $user_agent; } // set content-type // $accept as string function set_accept($accept = false) { $this->accept = $accept; } // set language // $language as string function set_language($language = false) { $this->language = $language; } // set charset // $charset as string function set_charset($charset = false) { $this->charset = $charset ; } // set referer // $referer as string function set_referer($referer = false) { $this->referer = $referer ; } // set cookies // $cookies as array // format: array("key1"=>"value1","key2"=>"value2") function set_cookies($cookies = false) { $this->cookies = $cookies ; } // set extra_header // $headers as array // format: array("key1: value","key2: value","key3: value") function set_extra_headers ($extra_headers = false) { $this->extra_headers = $extra_headers; } // set proxy, if set proxy will use // $proxy as string // format: http://user:pass@server:port function set_proxy($proxy = false) { $this->proxy = $proxy; } // set it to true and the request will only print out, not send, fsocket will not open // $debug as boolean function set_debug($debug = false) { $this->debug = $debug; } // reset all to defaults function reset() { $this->browser(); } function site($url) { return new site($url,$this->user_agent,$this->accept,$this->language,$this->charset,$this->referer,$this->cookies,$this->extra_headers,$this->debug,$this->proxy); } } class site { var $url; var $user_agent; var $accept; var $language; var $charset; var $referer; var $cookies; var $extra_headers; var $proxy; var $debug; var $scheme; var $host; var $port; var $path; var $user; var $pass; var $proxy_host; var $proxy_port; var $proxy_user; var $proxy_pass; // private var $socket; var $data; // contructor function site($url,$user_agent,$accept,$language,$charset,$referer,$cookies,$extra_headers,$debug,$proxy) { $this->url = $url; $this->user_agent = $user_agent; $this->accept = $accept; $this->language = $language; $this->charset = $charset; $this->referer = $referer; $this->cookies = $cookies; $this->extra_headers = $extra_headers; $this->proxy = $proxy; $this->debug = $debug; $this->scheme = false; $this->host = false; $this->port = false; $this->user = false; $this->pass = false; $this->proxy_host = false; $this->proxy_port = false; $this->proxy_user = false; $this->proxy_pass = false; // parse URL $url_parts = parse_url($this->url); $this->host = $url_parts['host']; // setting scheme if ( $url_parts['scheme'] ) { $this->scheme = $url_parts['scheme']; } else { $this->scheme = "http"; } // setting port if ( $url_parts['port'] ) { $this->port = $url_parts['port']; } else { $this->port = 80; } // setting path if ( $url_parts['path'] ) { $this->path = $url_parts['path']; } else { $this->path = '/'; } // adding query to path if ( $url_parts['query'] ) { $this->path = $this->path.'?'.$url_parts['query']; } // authentification if ( $url_parts['user'] ) { if ( $url_parts['pass']) { $this->pass = $url_parts['pass']; } else { $this->pass = ""; } $this->user = $url_parts['user']; } // parse proxy url if ($this->proxy) { $url_parts = parse_url($this->proxy); $this->proxy_host = $url_parts['host']; // setting port if ( $url_parts['port'] ) { $this->proxy_port = $url_parts['port']; } else { $this->proxy_port = 8080; } // authentification für proxy if ( $url_parts['user'] ) { if ( $url_parts['pass']) { $this->proxy_pass = $url_parts['pass']; } else { $this->proxy_pass = ""; } $this->proxy_user = $url_parts['user']; } } } // GET a site, return boolean function get() { if ($this->proxy) { $connect_host = $this->proxy_host; $connect_port = $this->proxy_port; } else { $connect_host = $this->host; $connect_port = $this->port; } if ( $this->_fsockopen($connect_host, $connect_port) ) { if ($this->proxy) { if ( $this->port != 80 ) { $header = 'GET '.$this->scheme.'://'.$this->host.$this->path.' HTTP/1.1'."\r\n"; } else { $header = 'GET '.$this->scheme.'://'.$this->host.':'.$this->port.$this->path.' HTTP/1.1'."\r\n"; } $header .= 'Host: '.$this->proxy_host."\r\n"; } else { $header = 'GET '.$this->path.' HTTP/1.0'."\r\n"; $header .= 'Host: '.$this->host."\r\n"; } if ( $this->user ) { $header .= 'Authorization: Basic '.base64_encode($this->user.':'.$this->pass)."\r\n"; } if ( $this->language !== false ) { $header .= 'Accept-Language: '.$this->language."\r\n"; } if ( $this->charset !== false ) { $header .= 'Accept-Charset: '.$this->charset."\r\n"; } if ( $this->user_agent !== false ) { $header .= 'User-Agent: '.$this->user_agent."\r\n"; } if ( $this->accept !== false ) { $header .= 'Accept: '.$this->accept."\r\n"; } if ( $this->referer !== false ) { $header .= 'Referer: '.$this->referer."\r\n"; } if ((is_array($this->extra_headers)) && (count($this->extra_headers))) { reset($this->extra_headers); foreach ($this->extra_headers as $extra_header) { if ( $extra_header ) { $header .= $extra_header."\r\n"; } } } if ((is_array($this->cookies)) && (count($this->cookies))) { $cookie = false; reset($this->cookies); foreach ($this->cookies as $var => $value) { if ( ($var) && ($value != "") ) { if ( ! $cookie) { $cookie = $var.'='.$value; } else { $cookie .= '; '.$var.'='.$value; } } } if ($cookie) { $header .= 'Cookie: '.$cookie."\r\n"; } } $header .= "\r\n\r\n"; $this->_fputs($header); $this->data = addslashes($this->_fgets()); $this->_fclose(); #echo $this->data; return $header; } else { return false; } } // HEAD a site, return boolean function head() { if ($this->proxy) { $connect_host = $this->proxy_host; $connect_port = $this->proxy_port; } else { $connect_host = $this->host; $connect_port = $this->port; } if ( $this->_fsockopen($connect_host, $connect_port) ) { if ($this->proxy) { if ( $this->port != 80 ) { $header = 'HEAD '.$this->scheme.'://'.$this->host.$this->path.' HTTP/1.1'."\r\n"; } else { $header = 'HEAD '.$this->scheme.'://'.$this->host.':'.$this->port.$this->path.' HTTP/1.1'."\r\n"; } $header .= 'Host: '.$this->proxy_host."\r\n"; } else { $header = 'HEAD '.$this->path.' '.' HTTP/1.1'."\r\n"; $header .= 'Host: '.$this->host."\r\n"; } if ( $this->user ) { $header .= 'Authorization: Basic '.base64_encode($this->user.':'.$this->pass)."\r\n"; } if ( $this->language !== false ) { $header .= 'Accept-Language: '.$this->language."\r\n"; } if ( $this->charset !== false ) { $header .= 'Accept-Charset: '.$this->charset."\r\n"; } if ( $this->user_agent !== false ) { $header .= 'User-Agent: '.$this->user_agent."\r\n"; } if ( $this->accept !== false ) { $header .= 'Accept: '.$this->accept."\r\n"; } if ( $this->referer !== false ) { $header .= 'Referer: '.$this->referer."\r\n"; } if ((is_array($this->extra_headers)) && (count($this->extra_headers))) { reset($this->extra_headers); foreach ($this->extra_headers as $extra_header) { if ( $extra_header ) { $header .= $extra_header."\r\n"; } } } if ((is_array($this->cookies)) && (count($this->cookies))) { $cookie = false; reset($this->cookies); foreach ($this->cookies as $var => $value) { if ( ($var) && ($value != "") ) { if ( ! $cookie) { $cookie = $var.'='.$value; } else { $cookie .= '; '.$var.'='.$value; } } } if ($cookie) { $header .= 'Cookie: '.$cookie."\r\n"; } } $header .= "Connection: close\r\n\r\n"; $this->_fputs($header); $this->data = $this->_fgets(); $this->_fclose(); return true; } else { return false; } } // POST a site, return boolean // $data data to send as array() // format: array("key1"=>"value1","key2"=>"value2") // $files files to send as array() // format: array(array("name"=>"file1","file"=>"/filename","type"=>"text/html","rename"="(optional) newfilename"),array(...)) // before post check that files exist and readable ! function post($data_to_send = false,$files_to_send = false) { if ($this->proxy) { $connect_host = $this->proxy_host; $connect_port = $this->proxy_port; } else { $connect_host = $this->host; $connect_port = $this->port; } if ( $this->_fsockopen($connect_host, $connect_port) ) { if ( ! is_array($data_to_send) ) { $data_to_send = array(); } if ( ! is_array($files_to_send) ) { $files_to_send = array(); } if ($this->proxy) { if ( $this->port != 80 ) { $header = 'POST '.$this->scheme.'://'.$this->host.$this->path.' HTTP/1.0'."\r\n"; } else { $header = 'POST '.$this->scheme.'://'.$this->host.':'.$this->port.$this->path.' HTTP/1.0'."\r\n"; } $header .= 'Host: '.$this->proxy_host."\r\n"; } else { $header = 'POST '.$this->path.' '.' HTTP/1.0'."\r\n"; $header .= 'Host: '.$this->host."\r\n"; } if ( $this->user ) { $header .= 'Authorization: Basic '.base64_encode($this->user.':'.$this->pass)."\r\n"; } if ( $this->language !== false ) { $header .= 'Accept-Language: '.$this->language."\r\n"; } if ( $this->charset !== false ) { $header .= 'Accept-Charset: '.$this->charset."\r\n"; } if ( $this->user_agent !== false ) { $header .= 'User-Agent: '.$this->user_agent."\r\n"; } if ( $this->accept !== false ) { $header .= 'Accept: '.$this->accept."\r\n"; } if ( $this->referer !== false ) { $header .= 'Referer: '.$this->referer."\r\n"; } if ((is_array($this->extra_headers)) && (count($this->extra_headers))) { reset($this->extra_headers); foreach ($this->extra_headers as $extra_header) { if ( $extra_header ) { $header .= $extra_header."\r\n"; } } } if ((is_array($this->cookies)) && (count($this->cookies))) { $cookie = false; reset($this->cookies); foreach ($this->cookies as $var => $value) { if ( ($var) && ($value != "") ) { if ( ! $cookie) { $cookie = $var.'='.$value; } else { $cookie .= '; '.$var.'='.$value; } } } if ($cookie) { $header .= 'Cookie: '.$cookie."\r\n"; } } $header .= "Connection: close\r\n"; $this->_fputs($header); if ( (count($data_to_send)) || (count($files_to_send)) ) { srand((double)microtime()*1000000); $boundary = "---------------------------".substr(md5(rand(0,32000)),0,10); $this->_fputs('Content-Type: multipart/form-data; boundary='.$boundary."\r\n"); $length = 0; // calculate Content-Length reset($data_to_send); foreach($data_to_send as $key=>$val) { $length += 2+strlen($boundary)+strlen($key)+strlen($val)+strlen('Content-Disposition: form-data; name=""')+8; } reset($files_to_send); foreach($files_to_send as $key=>$file) { if ( ! $file['rename']) { $file['rename'] = basename($file['file']); } $length += 2+strlen($boundary)+strlen('Content-Disposition: form-data; name="'.$file['name'].'"; filename="'.$file['rename'].'"')+strlen('Content-Type: '.$file['type'])+strlen('Content-Transfer-Encoding: binary')+10+filesize($file['file']); } $this->_fputs('Content-Length: '.strval($length)."\r\n\r\n"); if (count ($data_to_send)) { reset($data_to_send); foreach($data_to_send as $key=>$val) { $this->_fputs('--'.$boundary."\r\n"); $this->_fputs('Content-Disposition: form-data; name="'.$key.'"'."\r\n\r\n".$val."\r\n"); } } if (count ($files_to_send)) { reset($files_to_send); foreach($files_to_send as $file) { if ( (is_array($file)) && (count($file))) { if ( ! $file['rename']) { $file['rename'] = basename($file['file']); } $fh = fopen ($file['file'], "r"); $this->_fputs('--'.$boundary."\r\n"); $this->_fputs('Content-Disposition: form-data; name="'.$file['name'].'"; filename="'.$file['rename'].'"'."\r\n"); $this->_fputs('Content-Type: '.$file['type']."\r\n"); $this->_fputs('Content-Transfer-Encoding: binary'."\r\n\r\n"); $this->_fputs(fread ($fh, filesize ($file['file']))."\r\n"); fclose ($fh); } } } $this->_fputs('--'.$boundary.'--'); } $this->data = $this->_fgets(); $this->_fclose(); return true; } else { return false; } } // return http-status as string function get_status() { $head = $this->get_headers(); reset($head); foreach($head as $headline) { if (preg_match('/HTTP\/(.*) ([0-9][0-9][0-9])(.*)/i',$headline)) { preg_match ("/HTTP\/(.*) ([0-9][0-9][0-9])(.*)/i",$headline,$tmp); if ((isset($tmp[2])) && (is_numeric($tmp[2]))) { return $tmp[2]; } } } return false; } // return content-type as string // if $full == true return full content-type incl. q, otherwise only type (*/*) function get_type($full = false) { $head = $this->get_headers(); reset($head); foreach($head as $headline) { if (preg_match('/^Content-Type: /i',$headline)) { $type = preg_replace('/^Content-Type: /i', '', $headline); if ($full) { return $type; } else { $return = split(";",$type); return $return[0]; } } } return false; } // return content-lenght as integer function get_length() { $head = $this->get_headers(); reset($head); foreach($head as $headline) { if (preg_match('/^Content-Length: /i',$headline)) { $length = preg_replace('/^Content-Length: /i', '', $headline); return $length; } } return false; } // return new location if set function get_location() { $head = $this->get_headers(); reset($head); foreach($head as $headline) { if (preg_match('/^Location: /i',$headline)) { $location = preg_replace('/^Location: /i', '', $headline); return $location; } } return false; } // return cookies as muti-array or false // format: array(array("name"=>"foo","value"=>"bar","path"=>"string","time"=>timestamp,"domain"=>"string","secure"=>boolean),array(...)) function get_cookies() { $head = $this->get_headers(); $cookies = false; reset($head); foreach($head as $headline) { if (preg_match('/^Set-Cookie: /i',$headline)) { if (! is_array($cookies) ) { $cookies = array(); } $headline = trim($headline); $headline = preg_replace("/^Set-Cookie: /i", "", $headline); $cookiesplit = split(";",$headline); $cookieinfo = array(); // avr und value list($cookieinfo['name'],$cookieinfo['value']) = split("=",$cookiesplit[0],2); // zeit als timestamp if ( $cookiesplit[1]) { $cookieinfo['time'] = strtotime(preg_replace("/^expires=/i", "", trim($cookiesplit[1]))); } // path if ( $cookiesplit[2]) { $cookieinfo['path'] = preg_replace("/^path=/i", "", trim($cookiesplit[2])); } //domain if ( $cookiesplit[3]) { $cookieinfo['domain'] = preg_replace("/^domain=/i", "", trim($cookiesplit[3])); } // secure if ( strtolower(trim($cookiesplit[4]))=="secure") { $cookieinfo['secure'] = true; } $cookies[] = $cookieinfo; } } return $cookies; } // return all headers as array // format: array("HTTP/1.1 200 OK","Date: Thu, 27 May 2004 20:33:10 GMT") function get_headers() { return split ("\r\n",$this->get_header()); } // return date // return string like "Date: Thu, 27 May 2004 20:33:10 GMT" function get_date() { $head = $this->get_headers(); reset($head); foreach($head as $headline) { if (preg_match('/^Date: /i',$headline)) { $date = preg_replace('/^Date: /i', '', $headline); return $date; } } return false; } // return complete header as string function get_header() { $tmp = split ("\r\n\r\n", $this->data,2); if (isset($tmp[0])) { return $tmp[0]; } else { return ""; } } // return content as string function get_content() { $tmp = split ("\r\n\r\n", $this->data,2); if (isset($tmp[1])) { return $tmp[1]; } else { return ""; } } // intern, for debugging function _fsockopen($host, $port) { if ( ! $this->debug) { if ( $this->socket = fsockopen($host, $port)) { return true; } else { return false; } } else { return true; } } // intern, for debugging function _fputs($data) { if ( ! $this->debug) { fputs($this->socket, $data); } else { echo $data; } } // intern, for debugging function _fgets() { if ( ! $this->debug) { while(!feof($this->socket)) { $return .= fgets($this->socket, 512); } return $return; } else { return false; } } // intern, for debugging function _fclose() { if ( ! $this->debug) { fclose($this->socket); } } } ?>