PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/root/usr/share/nethesis/NethServer/Tool/phpprintipp/http_class.php

https://github.com/nethesis/nethserver-cups
PHP | 615 lines | 491 code | 28 blank | 96 comment | 62 complexity | e4cf503278eea0bac7bb22334b9380eb MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /* @(#) $Header: /sources/phpprintipp/phpprintipp/php_classes/http_class.php,v 1.7 2010/08/22 15:45:17 harding Exp $ */
  3. /* vim: set expandtab tabstop=2 shiftwidth=2 foldmethod=marker: */
  4. /* ====================================================================
  5. * GNU Lesser General Public License
  6. * Version 2.1, February 1999
  7. *
  8. * Class http_class - Basic http client with "Basic" and Digest/MD5
  9. * authorization mechanism.
  10. * handle ipv4/v6 addresses, Unix sockets, http and https
  11. * have file streaming capability, to cope with php "memory_limit"
  12. *
  13. * Copyright (C) 2006,2007,2008 Thomas HARDING
  14. *
  15. * This library is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU Lesser General Public
  17. * License as published by the Free Software Foundation; either
  18. * version 2.1 of the License, or (at your option) any later version.
  19. *
  20. * This library is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23. * Lesser General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU Lesser General Public
  26. * License along with this library; if not, write to the Free Software
  27. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  28. * $Id: http_class.php,v 1.7 2010/08/22 15:45:17 harding Exp $
  29. */
  30. /**
  31. * This class is intended to implement a subset of Hyper Text Transfer Protocol
  32. * (HTTP/1.1) on client side  (currently: POST operation), with file streaming
  33. * capability.
  34. *
  35. * It can perform Basic and Digest authentication.
  36. *
  37. * References needed to debug / add functionnalities:
  38. * - RFC 2616
  39. * - RFC 2617
  40. *
  41. *
  42. * Class and Function List:
  43. * Function list:
  44. * - __construct()
  45. * - getErrorFormatted()
  46. * - getErrno()
  47. * - __construct()
  48. * - GetRequestArguments()
  49. * - Open()
  50. * - SendRequest()
  51. * - ReadReplyHeaders()
  52. * - ReadReplyBody()
  53. * - Close()
  54. * - _StreamRequest()
  55. * - _ReadReply()
  56. * - _ReadStream()
  57. * - _BuildDigest()
  58. * Classes list:
  59. * - httpException extends Exception
  60. * - http_class
  61. */
  62. /***********************
  63. *
  64. * httpException class
  65. *
  66. ************************/
  67. class httpException extends Exception
  68. {
  69. protected $errno;
  70. public function __construct ($msg, $errno = null)
  71. {
  72. parent::__construct ($msg);
  73. $this->errno = $errno;
  74. }
  75. public function getErrorFormatted ()
  76. {
  77. return sprintf ("[http_class]: %s -- "._(" file %s, line %s"),
  78. $this->getMessage (), $this->getFile (), $this->getLine ());
  79. }
  80. public function getErrno ()
  81. {
  82. return $this->errno;
  83. }
  84. }
  85. function error2string($value)
  86. {
  87. $level_names = array(
  88. E_ERROR => 'E_ERROR',
  89. E_WARNING => 'E_WARNING',
  90. E_PARSE => 'E_PARSE',
  91. E_NOTICE => 'E_NOTICE',
  92. E_CORE_ERROR => 'E_CORE_ERROR',
  93. E_CORE_WARNING => 'E_CORE_WARNING',
  94. E_COMPILE_ERROR => 'E_COMPILE_ERROR',
  95. E_COMPILE_WARNING => 'E_COMPILE_WARNING',
  96. E_USER_ERROR => 'E_USER_ERROR',
  97. E_USER_WARNING => 'E_USER_WARNING',
  98. E_USER_NOTICE => 'E_USER_NOTICE'
  99. );
  100. if(defined('E_STRICT')) $level_names[E_STRICT]='E_STRICT';
  101. $levels=array();
  102. if(($value&E_ALL)==E_ALL)
  103. {
  104. $levels[]='E_ALL';
  105. $value&=~E_ALL;
  106. }
  107. foreach($level_names as $level=>$name)
  108. if(($value&$level)==$level) $levels[]=$name;
  109. return implode(' | ',$levels);
  110. }
  111. /***********************
  112. *
  113. * class http_class
  114. *
  115. ************************/
  116. class http_class
  117. {
  118. // variables declaration
  119. public $debug;
  120. public $html_debug;
  121. public $timeout = 30; // time waiting for connection, seconds
  122. public $data_timeout = 30; // time waiting for data, milliseconds
  123. public $data_chunk_timeout = 1; // time waiting between data chunks, millisecond
  124. public $force_multipart_form_post;
  125. public $username;
  126. public $password;
  127. public $request_headers = array ();
  128. public $request_body = "Not a useful information";
  129. public $status;
  130. public $window_size = 1024; // chunk size of data
  131. public $with_exceptions = 0; // compatibility mode for old scripts
  132. public $port;
  133. public $host;
  134. private $default_port = 631;
  135. private $headers;
  136. private $reply_headers = array ();
  137. private $reply_body = array ();
  138. private $connection;
  139. private $arguments;
  140. private $bodystream = array ();
  141. private $last_limit;
  142. private $connected;
  143. private $nc = 1;
  144. private $user_agent = "PRINTIPP/0.81+CVS";
  145. private $readed_bytes = 0;
  146. public function __construct ()
  147. {
  148. true;
  149. }
  150. /*********************
  151. *
  152. * Public functions
  153. *
  154. **********************/
  155. public function GetRequestArguments ($url, &$arguments)
  156. {
  157. $this->arguments = array ();
  158. $arguments["URL"] = $this->arguments["URL"] = $url;
  159. $arguments["RequestMethod"] = $this->arguments["RequestMethod"] = "POST";
  160. $this->headers["Content-Length"] = 0;
  161. $this->headers["Content-Type"] = "application/octet-stream";
  162. $this->headers["Host"] = $this->host;
  163. $this->headers["User-Agent"] = $this->user_agent;
  164. //$this->headers["Expect"] = "100-continue";
  165. }
  166. public function Open ($arguments)
  167. {
  168. $this->connected = false;
  169. $url = $arguments["URL"];
  170. $port = $this->default_port;
  171. #$url = split (':', $url, 2);
  172. $url = preg_split ('#:#', $url, 2);
  173. $transport_type = $url[0];
  174. $unix = false;
  175. switch ($transport_type)
  176. {
  177. case 'http':
  178. $transport_type = 'tcp://';
  179. break;
  180. case 'https':
  181. $transport_type = 'tls://';
  182. break;
  183. case 'unix':
  184. $transport_type = 'unix://';
  185. $port = 0;
  186. $unix = true;
  187. break;
  188. default:
  189. $transport_type = 'tcp://';
  190. break;
  191. }
  192. $url = $url[1];
  193. if (!$unix)
  194. {
  195. #$url = split ("/", preg_replace ("#^/{1,}#", '', $url), 2);
  196. $url = preg_split ("#/#", preg_replace ("#^/{1,}#", '', $url), 2);
  197. $url = $url[0];
  198. $port = $this->port;
  199. $error = sprintf (_("Cannot resolve url: %s"), $url);
  200. $ip = gethostbyname ($url);
  201. $ip = @gethostbyaddr ($ip);
  202. if (!$ip)
  203. {
  204. return $this->_HttpError ($error, E_USER_WARNING);
  205. }
  206. if (strstr ($url, ":")) // we got an ipv6 address
  207. if (!strstr ($url, "[")) // it is not escaped
  208. $url = sprintf ("[%s]", $url);
  209. }
  210. $this->connection = @fsockopen ($transport_type.$url, $port, $errno, $errstr, $this->timeout);
  211. $error =
  212. sprintf (_('Unable to connect to "%s%s port %s": %s'), $transport_type,
  213. $url, $port, $errstr);
  214. if (!$this->connection)
  215. {
  216. return $this->_HttpError ($error, E_USER_WARNING);
  217. }
  218. $this->connected = true;
  219. return array (true, "success");
  220. }
  221. public function SendRequest ($arguments)
  222. {
  223. $error =
  224. sprintf (_('Streaming request failed to %s'), $arguments['RequestURI']);
  225. $result = self::_StreamRequest ($arguments);
  226. if (!$result[0])
  227. {
  228. return $this->_HttpError ($error." ".$result[1], E_USER_WARNING);
  229. }
  230. self::_ReadReply ();
  231. if (!preg_match ('#http/1.1 401 unauthorized#', $this->status))
  232. {
  233. return array (true, "success");
  234. }
  235. $headers = array_keys ($this->reply_headers);
  236. $error = _("need authentication but no mechanism provided");
  237. if (!in_array ("www-authenticate", $headers))
  238. {
  239. return $this->_HttpError ($error, E_USER_WARNING);
  240. }
  241. #$authtype = split (' ', $this->reply_headers["www-authenticate"]);
  242. $authtype = preg_split ('# #', $this->reply_headers["www-authenticate"]);
  243. $authtype = strtolower ($authtype[0]);
  244. switch ($authtype)
  245. {
  246. case 'basic':
  247. $pass = base64_encode ($this->user.":".$this->password);
  248. $arguments["Headers"]["Authorization"] = "Basic ".$pass;
  249. break;
  250. case 'digest':
  251. $arguments["Headers"]["Authorization"] = self::_BuildDigest ();
  252. break;
  253. default:
  254. $error =
  255. sprintf (_("need '%s' authentication mechanism, but have not"),
  256. $authtype[0]);
  257. return $this->_HttpError ($error, E_USER_WARNING);
  258. break;
  259. }
  260. self::Close ();
  261. self::Open ($arguments);
  262. $error =
  263. sprintf (_
  264. ('Streaming request failed to %s after a try to authenticate'),
  265. $url);
  266. $result = self::_StreamRequest ($arguments);
  267. if (!$result[0])
  268. {
  269. return $this->_HttpError ($error.": ".$result[1], E_USER_WARNING);
  270. }
  271. self::_ReadReply ();
  272. return array (true, "success");
  273. }
  274. public function ReadReplyHeaders (&$headers)
  275. {
  276. $headers = $this->reply_headers;
  277. }
  278. public function ReadReplyBody (&$body, $chunk_size)
  279. {
  280. $body = substr ($this->reply_body, $this->last_limit, $chunk_size);
  281. $this->last_limit += $chunk_size;
  282. }
  283. public function Close ()
  284. {
  285. if (!$this->connected)
  286. return;
  287. fclose ($this->connection);
  288. }
  289. /*********************
  290. *
  291. * Private functions
  292. *
  293. *********************/
  294. private function _HttpError ($msg, $level, $errno = null)
  295. {
  296. $trace = '';
  297. $backtrace = debug_backtrace;
  298. foreach ($backtrace as $trace)
  299. {
  300. $trace .= sprintf ("in [file: '%s'][function: '%s'][line: %s];\n", $trace['file'], $trace['function'],$trace['line']);
  301. }
  302. $msg = sprintf ( '%s\n%s: [errno: %s]: %s',
  303. $trace, error2string ($level), $errno, $msg);
  304. if ($this->with_exceptions)
  305. {
  306. throw new httpException ($msg, $errno);
  307. }
  308. else
  309. {
  310. trigger_error ($msg, $level);
  311. return array (false, $msg);
  312. }
  313. }
  314. private function _streamString ($string)
  315. {
  316. $success = fwrite ($this->connection, $string);
  317. if (!$success)
  318. {
  319. return false;
  320. }
  321. return true;
  322. }
  323. private function _StreamRequest ($arguments)
  324. {
  325. $this->status = false;
  326. $this->reply_headers = array ();
  327. $this->reply_body = "";
  328. if (!$this->connected)
  329. {
  330. return _HttpError (_("not connected"), E_USER_WARNING);
  331. }
  332. $this->arguments = $arguments;
  333. $content_length = 0;
  334. foreach ($this->arguments["BodyStream"] as $argument)
  335. {
  336. list ($type, $value) = each ($argument);
  337. reset ($argument);
  338. if ($type == "Data")
  339. {
  340. $length = strlen ($value);
  341. }
  342. elseif ($type == "File")
  343. {
  344. if (is_readable ($value))
  345. {
  346. $length = filesize ($value);
  347. }
  348. else
  349. {
  350. $length = 0;
  351. return
  352. _HttpError (sprintf (_("%s: file is not readable"), $value),
  353. E_USER_WARNING);
  354. }
  355. }
  356. else
  357. {
  358. $length = 0;
  359. return
  360. _HttpError (sprintf
  361. (_("%s: not a valid argument for content"), $type),
  362. E_USER_WARNING);
  363. }
  364. $content_length += $length;
  365. }
  366. $this->request_body = sprintf (_("%s Bytes"), $content_length);
  367. $this->headers["Content-Length"] = $content_length;
  368. $this->arguments["Headers"] =
  369. array_merge ($this->headers, $this->arguments["Headers"]);
  370. if ($this->arguments["RequestMethod"] != "POST")
  371. {
  372. return
  373. _HttpError (sprintf
  374. (_("%s: method not implemented"),
  375. $arguments["RequestMethod"]), E_USER_WARNING);
  376. }
  377. $string =
  378. sprintf ("POST %s HTTP/1.1\r\n", $this->arguments["RequestURI"]);
  379. $this->request_headers[$string] = '';
  380. if (!$this->_streamString ($string))
  381. {
  382. return _HttpError (_("Error while puts POST operation"),
  383. E_USER_WARNING);
  384. }
  385. foreach ($this->arguments["Headers"] as $header => $value)
  386. {
  387. $string = sprintf ("%s: %s\r\n", $header, $value);
  388. $this->request_headers[$header] = $value;
  389. if (!$this->_streamString ($string))
  390. {
  391. return _HttpError (_("Error while puts HTTP headers"),
  392. E_USER_WARNING);
  393. }
  394. }
  395. $string = "\r\n";
  396. if (!$this->_streamString ($string))
  397. {
  398. return _HttpError (_("Error while ends HTTP headers"),
  399. E_USER_WARNING);
  400. }
  401. foreach ($this->arguments["BodyStream"] as $argument)
  402. {
  403. list ($type, $value) = each ($argument);
  404. reset ($argument);
  405. if ($type == "Data")
  406. {
  407. $streamed_length = 0;
  408. while ($streamed_length < strlen ($value))
  409. {
  410. $string = substr ($value, $streamed_length, $this->window_size);
  411. if (!$this->_streamString ($string))
  412. {
  413. return _HttpError (_("error while sending body data"),
  414. E_USER_WARNING);
  415. }
  416. $streamed_length += $this->window_size;
  417. }
  418. }
  419. elseif ($type == "File")
  420. {
  421. if (is_readable ($value))
  422. {
  423. $file = fopen ($value, 'rb');
  424. while (!feof ($file))
  425. {
  426. if (gettype ($block = @fread ($file, $this->window_size)) !=
  427. "string")
  428. {
  429. return _HttpError (_("cannot read file to upload"),
  430. E_USER_WARNING);
  431. }
  432. if (!$this->_streamString ($block))
  433. {
  434. return _HttpError (_("error while sending body data"),
  435. E_USER_WARNING);
  436. }
  437. }
  438. }
  439. }
  440. }
  441. return array (true, "success");
  442. }
  443. private function _ReadReply ()
  444. {
  445. if (!$this->connected)
  446. {
  447. return array (false, _("not connected"));
  448. }
  449. $this->reply_headers = array ();
  450. $this->reply_body = "";
  451. $headers = array ();
  452. $body = "";
  453. while (!feof ($this->connection))
  454. {
  455. $line = fgets ($this->connection, 1024);
  456. if (strlen (trim($line)) == 0)
  457. break; // \r\n => end of headers
  458. if (preg_match ('#^[[:space:]]#', $line))
  459. {
  460. $headers[-1] .= sprintf(' %s', trim ($line));
  461. continue;
  462. }
  463. $headers[] = trim ($line);
  464. }
  465. $this->status = isset ($headers[0]) ? strtolower ($headers[0]) : false;
  466. foreach ($headers as $header)
  467. {
  468. $header = preg_split ("#: #", $header);
  469. $header[0] = strtolower ($header[0]);
  470. if ($header[0] !== "www-authenticate")
  471. {
  472. $header[1] = isset ($header[1]) ? strtolower ($header[1]) : "";
  473. }
  474. if (!isset ($this->reply_headers[$header[0]]))
  475. {
  476. $this->reply_headers[$header[0]] = $header[1];
  477. }
  478. }
  479. self::_ReadStream ();
  480. return true;
  481. }
  482. private function _ReadStream ()
  483. {
  484. if (! array_key_exists ("content-length", $this->reply_headers))
  485. {
  486. stream_set_blocking($this->connection, 0);
  487. $this->reply_body = stream_get_contents($this->connection);
  488. return true;
  489. }
  490. stream_set_blocking($this->connection, 1);
  491. $content_length = $this->reply_headers["content-length"];
  492. $this->reply_body = stream_get_contents($this->connection,$content_length);
  493. return true;
  494. }
  495. private function _BuildDigest ()
  496. {
  497. $auth = $this->reply_headers["www-authenticate"];
  498. #list ($head, $auth) = split (" ", $auth, 2);
  499. list ($head, $auth) = preg_split ("# #", $auth, 2);
  500. #$auth = split (", ", $auth);
  501. $auth = preg_split ("#, #", $auth);
  502. foreach ($auth as $sheme)
  503. {
  504. #list ($sheme, $value) = split ('=', $sheme);
  505. list ($sheme, $value) = preg_split ('#=#', $sheme);
  506. $fields[$sheme] = trim (trim ($value), '"');
  507. }
  508. $nc = sprintf ('%x', $this->nc);
  509. $prepend = "";
  510. while ((strlen ($nc) + strlen ($prepend)) < 8)
  511. $prependi .= "0";
  512. $nc = $prepend.$nc;
  513. $cnonce = "printipp";
  514. $username = $this->user;
  515. $password = $this->password;
  516. $A1 = $username.":".$fields["realm"].":".$password;
  517. if (array_key_exists ("algorithm", $fields))
  518. {
  519. $algorithm = strtolower ($fields["algorithm"]);
  520. switch ($algorithm)
  521. {
  522. case "md5":
  523. break;
  524. case "md5-sess":
  525. $A1 =
  526. $username.":".$fields["realm"].":".$password.":".
  527. $fields['nonce'].":".$cnonce;
  528. break;
  529. default:
  530. return _HttpError(
  531. sprintf (_("digest Authorization: algorithm '%s' not implemented"),
  532. $algorithm),
  533. E_USER_WARNING);
  534. return false;
  535. break;
  536. }
  537. }
  538. $A2 = "POST:".$this->arguments["RequestURI"];
  539. if (array_key_exists ("qop", $fields))
  540. {
  541. $qop = strtolower ($fields["qop"]);
  542. #$qop = split (" ", $qop);
  543. $qop = preg_split ("# #", $qop);
  544. if (in_array ("auth", $qop))
  545. $qop = "auth";
  546. else
  547. {
  548. self::_HttpError(
  549. sprintf (_("digest Authorization: algorithm '%s' not implemented"),
  550. $qop),
  551. E_USER_WARNING);
  552. return false;
  553. }
  554. }
  555. $response = md5 (md5 ($A1).":".$fields["nonce"].":".md5 ($A2));
  556. if (isset ($qop) && ($qop == "auth"))
  557. {
  558. $response =
  559. md5 (md5 ($A1).":".$fields["nonce"].":".$nc.":".$cnonce.":".$qop.
  560. ":".$A2);
  561. }
  562. $auth_scheme =
  563. sprintf
  564. ('Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s"',
  565. $username, $fields["realm"], $fields['nonce'],
  566. $this->arguments["RequestURI"], $response);
  567. if (isset ($algorithm))
  568. $auth_scheme .= sprintf (', algorithm="%s"', $algorithm);
  569. if (isset ($qop))
  570. $auth_scheme .= sprintf (', cnonce="%s"', $cnonce);
  571. if (array_key_exists ("opaque", $fields))
  572. $auth_scheme .= sprintf (', opaque="%s"', $fields['opaque']);
  573. if (isset ($qop))
  574. $auth_scheme .= sprintf (', qop="%s"', $qop);
  575. $auth_scheme .= sprintf (', nc=%s', $nc);
  576. $this->nc++;
  577. return $auth_scheme;
  578. }
  579. };
  580. /*
  581. * Local variables:
  582. * mode: php
  583. * tab-width: 2
  584. * c-basic-offset: 2
  585. * End:
  586. */
  587. ?>