diff options
Diffstat (limited to 'Moodle/mod/print/http_class.php')
-rwxr-xr-x | Moodle/mod/print/http_class.php | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/Moodle/mod/print/http_class.php b/Moodle/mod/print/http_class.php new file mode 100755 index 0000000..f0285af --- /dev/null +++ b/Moodle/mod/print/http_class.php @@ -0,0 +1,629 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ + /* @(#) $Header: /sources/phpprintipp/phpprintipp/php_classes/http_class.php,v 1.1 2008/06/21 00:30:58 harding Exp $ + * + * + * Class http_class - Basic http client with "Basic" authorization mechanism. + * handle ipv6 addresses and https + * + * Copyright (C) 2006 Thomas HARDING + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * mailto:thomas.harding@laposte.net + * Thomas Harding, 56 rue de la bourie rouge, 45 000 ORLEANS -- FRANCE + * + */ + +/* + + This class is intended to implement a subset of Hyper Text Transfer Protocol (HTTP/1.1) on client side. + (currently: POST operation) + It is a replacement for http://www.phpclasses.org/browse/package/3.html + in versions post- + + It can perform Basic and Digest authentication. + It was tested only in clear mode + + References needed to debug / add functionnalities: + - RFC 2616 + - RFC 2617 +*/ +/* + TODO: beta tests on servers other than loopback one +*/ + +/*********************** +* +* httpException class +* +************************/ + + // {{{ class httpException +class httpException extends Exception { + + protected $errno; + + public function __construct($msg,$errno=null) { + parent :: __construct($msg); + $this->errno = $errno; + } + + public function getErrorFormatted() { + + $return = sprintf("[http_class]: %s -- "._(" file %s, line %s"), + $this->getMessage(), + $this->getFile(), + $this->getLine()); + + return $return; + } + + public function getErrno() { + + return $this->errno ; + } + +} + // }}} + +/************************* +* +* class http_class +* +**************************/ + +class http_class { + + // {{{ variables declaration + public $debug; + public $html_debug; + public $timeout = 30; // time waiting for connection, seconds + public $data_timeout = 30; // time waiting for data, milliseconds + public $force_multipart_form_post; + public $username; + public $password; + public $request_headers = array(); + public $request_body = "Not a useful information"; + public $status; + public $window_size = 10000; // chunk size of data + public $with_exceptions = 0; // compatibility mode for old scripts + public $port; + public $host; + + private $default_port = 631; + private $headers; + private $reply_headers = array(); + private $reply_body = array(); + private $connection; + private $arguments; + private $bodystream = array(); + private $last_limit; + private $connected; + private $nc = 1; + private $user_agent = "PRINTIPP/0.81"; + // }}} + + // {{{ constructor + public function __construct() { + true; + } + // }}} + +/********************* +* +* Public functions +* +**********************/ + + // {{{ GetRequestArguments ($url,&$arguments) + public function GetRequestArguments ($url,&$arguments) { + + $this->arguments = array(); + + $arguments["URL"] = $this->arguments["URL"] = $url; + $arguments["RequestMethod"] = $this->arguments["RequestMethod"] = "POST"; + $this->headers["Content-Length"] = 0; + $this->headers["Content-Type"] = "application/octet-stream"; + $this->headers["Host"] = $this->host; + $this->headers["User-Agent"] = $this->user_agent; + //$this->headers["Expect"] = "100-continue"; + } + // }}} + + // {{{ Open ($arguments) + public function Open ($arguments) { + $this->connected = false; + if (!$this->timeout) + $this->timeout = 30; + $url = $arguments["URL"]; + $port = $this->default_port; + + $url = split (':',$url,2); + $transport_type = $url[0]; + $unix = false; + switch($transport_type) { + case 'http': + $transport_type = 'tcp://'; + break; + case 'https': + $transport_type = 'tls://'; + break; + case 'unix': + $transport_type = 'unix://'; + $port = 0; + $unix = true; + break; + default: + $transport_type = 'tcp://'; + break; + } + $url = $url[1]; + if (!$unix) { + $url = split("/",preg_replace("#^/{1,}#",'',$url),2); + $url = $url[0]; + $port = $this->port; + $error = sprintf(_("Cannot resolve url: %s"),$url); + $ip = gethostbyname($url); + $ip = @gethostbyaddr($ip); + if (!$ip) + if ($this->with_exceptions) + throw new httpException($error); + else + { + trigger_error($error,E_USER_WARNING); + return false; + } + if (strstr($url,":")) // we got an ipv6 address + if (!strstr($url,"[")) // it is not escaped + $url = sprintf("[%s]",$url); + } + $this->connection = @fsockopen($transport_type.$url, $port, $errno, $errstr, $this->timeout); + $error = sprintf (_('Unable to connect to "%s%s port %s": %s'), + $transport_type, + $url, + $port, + $errstr); + + if (!$this->connection) + if ($this->with_exceptions) + { + $this->connected = false; + throw new httpException($error,$errno); + } + else + { + trigger_error($error,E_USER_WARNING); + $this->connected = false; + return $error; + } + $this->connected = true; + } + // }}} + + // {{{ SendRequest($arguments) + public function SendRequest($arguments) { + + if (!$this->data_timeout) + $this->data_timeout = 30; + + if(!$result = self::_StreamRequest($arguments)) + return("SendRequest: unknown error"); + + self::_ReadReply(); + + if (!preg_match('#http/1.1 401 unauthorized#',$this->status)) + return; + + + $headers = array_keys ($this->reply_headers); + if (!in_array("www-authenticate",$headers)) + return("SendRequest: need authentication but no mechanism provided"); + + $authtype = split(' ',$this->reply_headers["www-authenticate"]); + $authtype = strtolower($authtype[0]); + + switch ($authtype) { + case 'basic': + $pass = base64_encode($this->user.":".$this->password); + $arguments["Headers"]["Authorization"] = "Basic ".$pass; + break; + case 'digest': + $arguments["Headers"]["Authorization"] = self::_BuildDigest(); + break; + default: + return sprintf(_("http_class: need '%s' authentication mechanism, but have not, sorry"),$authtype[0]); + } + + self::Close(); + self::Open($arguments); + + if(!$result = self::_StreamRequest($arguments)) + return("SendRequest: unknown error"); + + self::_ReadReply(); + + } + // }}} + + // {{{ ReadReplyHeaders (&$headers) + public function ReadReplyHeaders (&$headers) { + if ($this->connected) + $headers = $this->reply_headers; + } + // }}} + + // {{{ ReadReplyBody (&$body,$chunk_size) + public function ReadReplyBody (&$body,$chunk_size) { + $body = substr($this->reply_body,$this->last_limit,$chunk_size); + $this->last_limit += $chunk_size; + } + // }}} + + // {{{ Close () + public function Close () { + if (!$this->connected) return; + fclose ($this->connection); + } + // }}} + +/********************* +* +* Private functions +* +**********************/ + + // {{{ _StreamRequest ($arguments) + private function _StreamRequest ($arguments) { + if (!$this->connected) return; + + $this->status = ""; + $this->reply_headers = array(); + $this->reply_body = ""; + + $this->arguments = $arguments; + + $content_length = 0; + foreach ($this->arguments["BodyStream"] as $argument) { + + list($type,$value) = each($argument); + reset ($argument); + + if ($type == "Data") + $length = strlen($value); + + elseif ($type == "File") + if (is_readable($value)) + $length = filesize($value); + else { + $length = 0; + trigger_error(sprintf(_("%s: file is not readable"),$value),E_USER_WARNING); + } + + else { + $length = 0; + trigger_error(sprintf(_("%s: not a valid argument for content"),$type),E_USER_WARNING); + } + + $content_length += $length; + } + + $this->request_body = sprintf(_("%s Bytes"), $content_length); + $this->headers["Content-Length"] = $content_length; + $this->arguments["Headers"] = array_merge($this->headers,$this->arguments["Headers"]); + + //$read = array($this->connection); + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) { + // server talks! + trigger_error(_("http_class: server talk first, quit"),E_USER_WARNING); + return false; + } + + if ($this->arguments["RequestMethod"] != "POST") { + trigger_error (sprintf(_("%s: method not implemented"),$arguments["RequestMethod"]),E_USER_WARNING); + return sprintf(_("%s: method not implemented"),$arguments["RequestMethod"]); + } + + $string = sprintf("POST %s HTTP/1.1\r\n",$this->arguments["RequestURI"]); + $error = fwrite($this->connection,$string); + $this->request_headers[$string] = ''; + + //if(stream_select($read, $write = NULL, $except = NULL, 0) === 1) // server talks! + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) + return "server-talk"; + if (!$error) { + trigger_error(_("Error while puts first header"),E_USER_WARNING); + return _("Stream closed while puts first header"); + } + + foreach ($this->arguments["Headers"] as $header => $value) { + + $error = @fwrite($this->connection,sprintf("%s: %s\r\n", $header, $value)); + $this->request_headers[$header] = $value; + //if(stream_select($read, $write = NULL, $except = NULL, 0) === 1) + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) + return "server-talk"; + + if (!$error) { + trigger_error(_("Error while puts HTTP headers"),E_USER_WARNING); + return _("Stream closed while puts HTTP headers"); + } + } + + $error = fwrite($this->connection,"\r\n"); + //fflush($this->connection); + + //if($strselect = stream_select($read, $write = NULL, $except = NULL, 0,$this->data_timeout*1000) === 1) + //usleep($this->data_timeout*1000); + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) + return "server-talk"; + + if (!$error) { + trigger_error(_("Error while ends HTTP headers"),E_USER_WARNING); + return _("Stream closed while ends HTTP headers"); + } + + foreach ($this->arguments["BodyStream"] as $argument) { + + list($type,$value) = each($argument); + reset ($argument); + + + if ($type == "Data") { + $streamed_length = 0; + while ($streamed_length < strlen($value)) { + //if(stream_select($read, $write = NULL, $except = NULL, 0) === 1) + //usleep($this->data_timeout*1000); + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) + return "server-talk"; + + // not very clean... + $error = @fwrite($this->connection,substr($value,$streamed_length,$this->window_size)); + if (!$error) + return "error-while-push-data"; + $streamed_length += $this->window_size; + } + } + + if (!$error) + return _("error-while-push-data"); + + elseif ($type == "File") { + if (is_readable($value)) { + $file = fopen($value,'rb'); + while(!feof($file)) { + + if(gettype($block = @fread($file,$this->window_size)) != "string") { + trigger_error(_("cannot read file to upload"),E_USER_WARNING); + return _("cannot read file to upload"); + } + + //if(stream_select($read, $write = NULL, $except = NULL, 0) === 1) + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) + return "server-talks"; + + // not very clean... + $error = @fwrite($this->connection,$block); + if (!$error) + return "error-while-push-data"; + + } + } + } + + //if(stream_select($read, $write = NULL, $except = NULL, 0) === 1) + $status = stream_get_meta_data($this->connection); + if (isset($status['unread-bytes']) && $status['unread-bytes']) + return "server-talks"; + + } + + return true; + } + // }}} + + // {{{ _ReadReply () + private function _ReadReply () { + + if (!$this->connected) return; + + $this->reply_headers = array(); + $this->reply_body = ""; + + + + $line = "1\r\n"; + $headers = ""; + $body = ""; + while (!feof($this->connection)) { + $line = fgets($this->connection,1024); + if (strlen($line) <= 2) + break; + $headers .= $line; + } + $chunk = true; + + $headers = preg_split('#\r\n#',$headers); + + $this->status = strtolower($headers[0]); + + foreach($headers as $header) { + if (!preg_match('#www-authenticate: #i',$header)) + $header = strtolower($header); + + $header = preg_split("#: #",$header); + $header[0] = strtolower($header[0]); + + $this->reply_headers["{$header[0]}"] = array_key_exists(1,$header) ? $header[1] : ""; + } + unset ($this->reply_headers['']); + + //giving 3 chances to complete reading + $read = array($this->connection); + for ($i = 0 ; $i < 2 ; $i++) { + if (self::_ReadStream() === "completed") + break; + //$strselect = stream_select($read, $write = NULL, $except = NULL, 0,$this->data_timeout*1000); + //if (!$strselect) + //usleep($this->data_timeout*1000); + $status = stream_get_meta_data ($this->connection); + if ($status["unread_bytes"] == 0) + break; + } + + return true; + } + // }}} + + // {{{ _ReadStream () + private function _ReadStream () { + + $content_length = 0; + if (array_key_exists("content-length",$this->reply_headers)); + $content_length = $this->reply_headers["content-length"]; + + stream_set_blocking ($this->connection, 0 ); + usleep($this->data_timeout * 1000); + $total = 0; + $chunk = true; + while (true) { + if ($content_length) + if (strlen($this->reply_body) >= $content_length) + return "completed"; + else + if (!$chunk) + break; + usleep (1000); + $chunk = @fread($this->connection,$this->window_size); + $this->reply_body .= $chunk; + + $status = stream_get_meta_data ($this->connection); + if ($status["unread_bytes"] == 0) + break; + } + stream_set_blocking ($this->connection, 1 ); + return true; + } + // }}} + + // {{{ _BuildDigest () + private function _BuildDigest () { + + $auth = $this->reply_headers["www-authenticate"]; + + list($head, $auth) = split(" ",$auth,2); + + $auth=split(", ",$auth); + foreach ($auth as $sheme) { + list($sheme,$value) = split('=',$sheme); + $fields[$sheme] = trim(trim($value),'"'); + } + + $nc = sprintf('%x',$this->nc); + $prepend = ""; + while ((strlen($nc) + strlen($prepend)) < 8) + $prepend .= "0"; + $nc=$prepend.$nc; + + $cnonce = "printipp"; + + $username = $this->user; + $password = $this->password; + + $A1 = $username.":".$fields["realm"].":".$password; + + if (array_key_exists("algorithm",$fields)) { + $algorithm = strtolower($fields["algorithm"]); + switch ($algorithm) { + case "md5": + break; + case "md5-sess": + $A1 = $username.":".$fields["realm"].":".$password.":".$fields['nonce'].":".$cnonce; + break; + case "token": + trigger_error("http_class: digest Authorization: algorithm 'token' not implemented", E_USER_WARNING); + return false; + break; + } + + } + + $A2 = "POST:".$this->arguments["RequestURI"]; + + + if (array_key_exists("qop",$fields)) { + $qop = strtolower($fields["qop"]); + $qop = split(" ",$qop); + if (in_array("auth",$qop)) + $qop = "auth"; + else { + trigger_error("http_class: digest Authorization: qop others than 'auth' not implemented", E_USER_WARNING); + return false; + } + } + + //echo $A1,":",$fields["nonce"],":",$A2,"<br />"; + $response = md5(md5($A1).":". $fields["nonce"].":" .md5($A2)); + + if (isset($qop) && ($qop == "auth")) + { + $response = md5(md5($A1).":".$fields["nonce"].":".$nc.":".$cnonce.":".$qop.":".$A2); + } + + $auth_scheme = sprintf('Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"', + $username, + $fields["realm"], + $fields['nonce'], + $this->arguments["RequestURI"], + $response + ); + //echo $auth_scheme,"<br />"; + if (isset($algorithm)) + $auth_scheme .= sprintf(', algorithm="%s"',$algorithm); + + if (isset($qop)) + $auth_scheme .= sprintf(', cnonce="%s"',$cnonce); + + if(array_key_exists("opaque",$fields)) + $auth_scheme .= sprintf(', opaque="%s"',$fields['opaque']); + + if (isset($qop)) + $auth_scheme .= sprintf(', qop="%s"',$qop); + + $auth_scheme .= sprintf(', nc=%s',$nc); + + $this->nc++; + + return $auth_scheme; + } + // }}} + +}; + + +/* + * Local variables: + * mode: php + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ +?> |