PageRenderTime 26ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/HTTPRequest.class.php

https://github.com/zhuomingliang/phpdaemon
PHP | 843 lines | 592 code | 117 blank | 134 comment | 134 complexity | ba7a3751db29729af54ba347b18e2335 MD5 | raw file
  1. <?php
  2. /**
  3. * HTTP request
  4. *
  5. * @package Core
  6. *
  7. * @author Zorin Vasily <kak.serpom.po.yaitsam@gmail.com>
  8. */
  9. class HTTPRequest extends Request {
  10. /**
  11. * Status codes
  12. * @var array
  13. */
  14. private static $codes = array (
  15. 100 => 'Continue',
  16. 101 => 'Switching Protocols',
  17. 200 => 'OK',
  18. 201 => 'Created',
  19. 202 => 'Accepted',
  20. 203 => 'Non-Authoritative Information',
  21. 204 => 'No Content',
  22. 205 => 'Reset Content',
  23. 206 => 'Partial Content',
  24. 300 => 'Multiple Choices',
  25. 301 => 'Moved Permanently',
  26. 302 => 'Found',
  27. 303 => 'See Other',
  28. 304 => 'Not Modified',
  29. 305 => 'Use Proxy',
  30. 306 => '(Unused)',
  31. 307 => 'Temporary Redirect',
  32. 400 => 'Bad Request',
  33. 401 => 'Unauthorized',
  34. 402 => 'Payment Required',
  35. 403 => 'Forbidden',
  36. 404 => 'Not Found',
  37. 405 => 'Method Not Allowed',
  38. 406 => 'Not Acceptable',
  39. 407 => 'Proxy Authentication Required',
  40. 408 => 'Request Timeout',
  41. 409 => 'Conflict',
  42. 410 => 'Gone',
  43. 411 => 'Length Required',
  44. 412 => 'Precondition Failed',
  45. 413 => 'Request Entity Too Large',
  46. 414 => 'Request-URI Too Long',
  47. 415 => 'Unsupported Media Type',
  48. 416 => 'Requested Range Not Satisfiable',
  49. 417 => 'Expectation Failed',
  50. 500 => 'Internal Server Error',
  51. 501 => 'Not Implemented',
  52. 502 => 'Bad Gateway',
  53. 503 => 'Service Unavailable',
  54. 504 => 'Gateway Timeout',
  55. 505 => 'HTTP Version Not Supported',
  56. );
  57. // @todo phpdoc needed
  58. public $answerlen = 0;
  59. public $contentLength;
  60. private $cookieNUm = 0;
  61. public static $hvaltr = array(';' => '&', ' ' => '');
  62. public static $htr = array('-' => '_');
  63. public $mpartstate = 0;
  64. public $mpartoffset = 0;
  65. public $mpartcondisp = false;
  66. public $headers = array('STATUS' => '200 OK');
  67. public $headers_sent = false; // @todo make private
  68. private $boundary = false;
  69. /**
  70. * Preparing before init
  71. * @todo protected?
  72. * @param object Source request
  73. * @return void
  74. */
  75. public function preinit($req) {
  76. if ($req === null) {
  77. $req = new \stdClass;
  78. $req->attrs = new \stdClass;
  79. $req->attrs->stdin_done = true;
  80. $req->attrs->params_done = true;
  81. $req->attrs->chunked = false;
  82. }
  83. $this->attrs = $req->attrs;
  84. if ($this->upstream->config->expose->value) {
  85. $this->header('X-Powered-By: phpDaemon/' . Daemon::$version);
  86. }
  87. $this->parseParams();
  88. }
  89. /**
  90. * Called by call() to check if ready
  91. * @todo protected?
  92. * @return void
  93. */
  94. public function preCall() {
  95. if (
  96. !$this->attrs->params_done
  97. || !$this->attrs->stdin_done
  98. ) {
  99. $this->state = Request::STATE_SLEEPING;
  100. } else {
  101. if (isset($this->appInstance->passphrase)) {
  102. if (
  103. !isset($this->attrs->server['PASSPHRASE'])
  104. || ($this->appInstance->passphrase !== $this->attrs->server['PASSPHRASE'])
  105. ) {
  106. $this->finish();
  107. }
  108. }
  109. }
  110. }
  111. /**
  112. * Parses GET-query string and other request's headers
  113. * @todo private?
  114. * @return void
  115. */
  116. public function parseParams() {
  117. if (
  118. isset($this->attrs->server['CONTENT_TYPE'])
  119. && !isset($this->attrs->server['HTTP_CONTENT_TYPE'])
  120. ) {
  121. $this->attrs->server['HTTP_CONTENT_TYPE'] = $this->attrs->server['CONTENT_TYPE'];
  122. }
  123. if (isset($this->attrs->server['QUERY_STRING'])) {
  124. HTTPRequest::parse_str($this->attrs->server['QUERY_STRING'], $this->attrs->get);
  125. }
  126. if (
  127. isset($this->attrs->server['REQUEST_METHOD'])
  128. && ($this->attrs->server['REQUEST_METHOD'] == 'POST')
  129. && isset($this->attrs->server['HTTP_CONTENT_TYPE'])
  130. ) {
  131. parse_str(strtr($this->attrs->server['HTTP_CONTENT_TYPE'], HTTPRequest::$hvaltr), $contype);
  132. if (
  133. isset($contype['multipart/form-data'])
  134. && (isset($contype['boundary']))
  135. ) {
  136. $this->boundary = $contype['boundary'];
  137. }
  138. }
  139. if (isset($this->attrs->server['HTTP_COOKIE'])) {
  140. HTTPRequest::parse_str(strtr($this->attrs->server['HTTP_COOKIE'], HTTPRequest::$hvaltr), $this->attrs->cookie);
  141. }
  142. if (isset($this->attrs->server['HTTP_AUTHORIZATION'])) {
  143. $e = explode(' ', $this->attrs->server['HTTP_AUTHORIZATION'], 2);
  144. if (
  145. ($e[0] == 'Basic')
  146. && isset($e[1])
  147. ) {
  148. $e[1] = base64_decode($e[1]);
  149. $e = explode(':', $e[1], 2);
  150. if (isset($e[1])) {
  151. list($this->attrs->server['PHP_AUTH_USER'], $this->attrs->server['PHP_AUTH_PW']) = $e;
  152. }
  153. }
  154. }
  155. $this->onParsedParams();
  156. }
  157. /**
  158. * Prepares the request's body
  159. * @return void
  160. */
  161. public function postPrepare() {
  162. if (
  163. isset($this->attrs->server['REQUEST_METHOD'])
  164. && ($this->attrs->server['REQUEST_METHOD'] == 'POST')
  165. ) {
  166. if ($this->boundary === false) {
  167. HTTPRequest::parse_str($this->attrs->stdinbuf, $this->attrs->post);
  168. }
  169. if (isset($this->attrs->server['REQUEST_PREPARED_UPLOADS']) && $this->attrs->server['REQUEST_PREPARED_UPLOADS'] == 'nginx') {
  170. if (isset($this->attrs->server['REQUEST_PREPARED_UPLOADS_URL_PREFIX'])) {
  171. $URLprefix = $this->attrs->server['REQUEST_PREPARED_UPLOADS_URL_PREFIX'];
  172. $l = strlen($URLprefix);
  173. foreach (array('PHP_SELF', 'REQUEST_URI', 'SCRIPT_NAME', 'DOCUMENT_URI') as $k) {
  174. if (!isset($this->attrs->server[$k])) {continue;}
  175. if (strncmp($this->attrs->server[$k], $URLprefix, $l) === 0) {
  176. $this->attrs->server[$k] = substr($this->attrs->server[$k], $l-1);
  177. }
  178. }
  179. }
  180. $prefix = 'file.';
  181. $prefixlen = strlen($prefix);
  182. foreach ($this->attrs->post as $k => $v) {
  183. if (strncmp($k, $prefix, $prefixlen) === 0) {
  184. $e = explode('.', substr($k, $prefixlen));
  185. if (!isset($this->attrs->files[$e[0]])) {
  186. $this->attrs->files[$e[0]] = array('error' => UPLOAD_ERR_OK);
  187. }
  188. $this->attrs->files[$e[0]][$e[1]] = $v;
  189. }
  190. }
  191. foreach ($this->attrs->files as $k => $file) {
  192. if (!isset($file['tmp_name'])
  193. || !isset($file['name'])
  194. || !ctype_digit(basename($file['tmp_name']))
  195. || pathinfo($file['tmp_name'], PATHINFO_DIRNAME) !== ini_get('upload_tmp_dir'))
  196. {
  197. unset($this->attrs->files[$k]);
  198. continue;
  199. }
  200. $this->attrs->files[$k]['fp'] = fopen($file['tmp_name'], 'c+');
  201. }
  202. }
  203. if (
  204. isset($this->attrs->server['REQUEST_BODY_FILE'])
  205. && $this->upstream->config->autoreadbodyfile->value
  206. ) {
  207. $this->readBodyFile();
  208. }
  209. }
  210. }
  211. /**
  212. * @description Called when new piece of request's body is received
  213. * @param string Piece of request body
  214. * @return void
  215. */
  216. public function stdin($c) {
  217. if ($c !== '') {
  218. $this->attrs->stdinbuf .= $c;
  219. $this->attrs->stdinlen += strlen($c);
  220. }
  221. if (
  222. !isset($this->attrs->server['HTTP_CONTENT_LENGTH'])
  223. || ($this->attrs->server['HTTP_CONTENT_LENGTH'] <= $this->attrs->stdinlen)
  224. ) {
  225. $this->attrs->stdin_done = true;
  226. $this->postPrepare();
  227. }
  228. $this->parseStdin();
  229. }
  230. /**
  231. * Output some data
  232. * @param string String to out
  233. * @return boolean Success
  234. */
  235. public function out($s, $flush = true) {
  236. if ($flush) {
  237. ob_flush();
  238. }
  239. if ($this->aborted) {
  240. return false;
  241. }
  242. $l = strlen($s);
  243. $this->answerlen += $l;
  244. if (!$this->headers_sent) {
  245. if (isset($this->headers['STATUS'])) {
  246. $h = (isset($this->attrs->noHttpVer) && ($this->attrs->noHttpVer) ? 'Status: ' : $this->attrs->server['SERVER_PROTOCOL']) . ' ' . $this->headers['STATUS'] . "\r\n";
  247. } else {
  248. $h = '';
  249. }
  250. if ($this->attrs->chunked) {
  251. $this->header('Transfer-Encoding: chunked');
  252. }
  253. foreach ($this->headers as $k => $line) {
  254. if ($k !== 'STATUS') {
  255. $h .= $line . "\r\n";
  256. }
  257. }
  258. $h .= "\r\n";
  259. $this->headers_sent = true;
  260. if (!Daemon::$compatMode) {
  261. if (
  262. !$this->attrs->chunked
  263. && !$this->sendfp
  264. ) {
  265. return $this->upstream->requestOut($this, $h . $s);
  266. }
  267. $this->upstream->requestOut($this,$h);
  268. }
  269. }
  270. if ($this->attrs->chunked) {
  271. for ($o = 0; $o < $l;) {
  272. $c = min($this->upstream->config->chunksize->value, $l - $o);
  273. $chunk = dechex($c) . "\r\n"
  274. . ($c === $l ? $s : binarySubstr($s, $o, $c)) // content
  275. . "\r\n";
  276. if ($this->sendfp) {
  277. fwrite($this->sendfp, $chunk);
  278. } else {
  279. $this->upstream->requestOut($this, $chunk);
  280. }
  281. $o += $c;
  282. }
  283. } else {
  284. if ($this->sendfp) {
  285. fwrite($this->sendfp, $s);
  286. return true;
  287. }
  288. if (Daemon::$compatMode) {
  289. echo $s;
  290. return true;
  291. }
  292. return $this->upstream->requestOut($this, $s);
  293. }
  294. }
  295. /**
  296. * Called when request's headers parsed
  297. * @return void
  298. */
  299. public function onParsedParams() { }
  300. /**
  301. * Outputs data with headers (split by \r\n\r\n)
  302. * @todo description missed (what is the difference between this and out()) ?
  303. * @param string String to out
  304. * @return boolean Success.
  305. */
  306. public function combinedOut($s) {
  307. if (!$this->headers_sent) {
  308. $e = explode("\r\n\r\n", $s, 2);
  309. $h = explode("\r\n", $e[0]);
  310. foreach ($h as $l) {
  311. $this->header($l);
  312. }
  313. if (isset($e[1])) {
  314. return $this->out($e[1]);
  315. }
  316. return true;
  317. } else {
  318. return $this->out($s);
  319. }
  320. }
  321. /**
  322. * Use chunked encoding
  323. * @return void
  324. */
  325. public function chunked() {
  326. $this->header('Transfer-Encoding: chunked');
  327. $this->attrs->chunked = true;
  328. }
  329. /**
  330. * Called when the request wakes up
  331. * @return void
  332. */
  333. public function onWakeup() {
  334. parent::onWakeup();
  335. $_GET = &$this->attrs->get;
  336. $_POST = &$this->attrs->post;
  337. $_COOKIE = &$this->attrs->cookie;
  338. $_REQUEST = &$this->attrs->request;
  339. $_SESSION = &$this->attrs->session;
  340. $_FILES = &$this->attrs->files;
  341. $_SERVER = &$this->attrs->server;
  342. }
  343. /**
  344. * Called when the request starts sleep
  345. * @return void
  346. */
  347. public function onSleep() {
  348. parent::onSleep();
  349. unset($_GET);
  350. unset($_POST);
  351. unset($_COOKIE);
  352. unset($_REQUEST);
  353. unset($_SESSION);
  354. unset($_FILES);
  355. unset($_SERVER);
  356. }
  357. /**
  358. * Send HTTP-status
  359. * @throws RequestHeadersAlreadySent
  360. * @param int Code
  361. * @return void
  362. */
  363. public function status($code = 200) {
  364. if (!isset(self::$codes[$code])) {
  365. return false;
  366. }
  367. $this->header($code . ' ' . self::$codes[$code]);
  368. return true;
  369. }
  370. /**
  371. * Analog of standard PHP function headers_sent
  372. * @return boolean Success
  373. */
  374. public function headers_sent() {
  375. return $this->headers_sent;
  376. }
  377. /**
  378. * Return current list of headers
  379. * @return array Headers.
  380. */
  381. public function headers_list() {
  382. return array_values($this->headers);
  383. }
  384. /**
  385. * Set the cookie
  386. * @param string Name of cookie
  387. * @param string Value
  388. * @param integer. Optional. Max-Age. Default is 0.
  389. * @param string. Optional. Path. Default is empty string.
  390. * @param boolean. Optional. Secure. Default is false.
  391. * @param boolean. Optional. HTTPOnly. Default is false.
  392. * @return void
  393. * @throws RequestHeadersAlreadySent
  394. */
  395. public function setcookie($name, $value = '', $maxage = 0, $path = '', $domain = '', $secure = false, $HTTPOnly = false) {
  396. $this->header(
  397. 'Set-Cookie: ' . $name . '=' . rawurlencode($value)
  398. . (empty($domain) ? '' : '; Domain=' . $domain)
  399. . (empty($maxage) ? '' : '; Max-Age='.$maxage)
  400. . (empty($path) ? '' : '; Path='.$path)
  401. . (!$secure ? '' : '; Secure')
  402. . (!$HTTPOnly ? '' : '; HttpOnly'), false);
  403. }
  404. /**
  405. * Send the header
  406. * @param string Header. Example: 'Location: http://php.net/'
  407. * @param boolean Optional. Replace?
  408. * @param int Optional. HTTP response code.
  409. * @return void
  410. * @throws RequestHeadersAlreadySent
  411. */
  412. public function header($s, $replace = true, $code = false) {
  413. if ($code !== null) {
  414. $this->status($code);
  415. }
  416. if ($this->headers_sent) {
  417. throw new RequestHeadersAlreadySent();
  418. return false;
  419. }
  420. $e = explode(':', $s, 2);
  421. if (!isset($e[1])) {
  422. $e[0] = 'STATUS';
  423. if (strncmp($s, 'HTTP/', 5) === 0) {
  424. $s = substr($s, 9);
  425. }
  426. }
  427. $k = strtr(strtoupper($e[0]), HTTPRequest::$htr);
  428. if ($k === 'SET_COOKIE') {
  429. $k .= '_'.++$this->cookieNum;
  430. }
  431. elseif (
  432. !$replace
  433. && isset($this->headers[$k])
  434. ) {
  435. return false;
  436. }
  437. $this->headers[$k] = $s;
  438. if ($k === 'CONTENT_LENGTH') {
  439. $this->contentLength = (int) $e[1];
  440. }
  441. elseif ($k === 'LOCATION') {
  442. $this->status(301);
  443. }
  444. if (Daemon::$compatMode) {
  445. is_callable('header_native') ? header_native($s) : header($s);
  446. }
  447. return true;
  448. }
  449. /**
  450. * @todo description missing
  451. */
  452. public function parseSize($value) {
  453. $l = strtolower(substr($value, -1));
  454. if ($l === 'b') {
  455. return ((int) substr($value, 0, -1));
  456. }
  457. if ($l === 'k') {
  458. return ((int) substr($value, 0, -1) * 1024);
  459. }
  460. if ($l === 'm') {
  461. return ((int) substr($value, 0, -1) * 1024 * 1024);
  462. }
  463. if ($l === 'g') {
  464. return ((int) substr($value, 0, -1) * 1024 * 1024 * 1024);
  465. }
  466. return (int) $value;
  467. }
  468. /**
  469. * Parse request body
  470. * @return void
  471. */
  472. public function parseStdin() {
  473. do {
  474. if ($this->boundary === false) {
  475. break;
  476. }
  477. $continue = false;
  478. if ($this->mpartstate === 0) {
  479. // seek to the nearest boundary
  480. if (($p = strpos($this->attrs->stdinbuf, $ndl = '--' . $this->boundary . "\r\n", $this->mpartoffset)) !== false) {
  481. // we have found the nearest boundary at position $p
  482. $this->mpartoffset = $p + strlen($ndl);
  483. $this->mpartstate = 1;
  484. $continue = true;
  485. }
  486. }
  487. elseif ($this->mpartstate === 1) {
  488. // parse the part's headers
  489. $this->mpartcondisp = false;
  490. if (($p = strpos($this->attrs->stdinbuf, "\r\n\r\n", $this->mpartoffset)) !== false) {
  491. // we got all of the headers
  492. $h = explode("\r\n", binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p-$this->mpartoffset));
  493. $this->mpartoffset = $p + 4;
  494. $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset);
  495. $this->mpartoffset = 0;
  496. for ($i = 0, $s = sizeof($h); $i < $s; ++$i) {
  497. $e = explode(':', $h[$i], 2);
  498. $e[0] = strtr(strtoupper($e[0]), HTTPRequest::$htr);
  499. if (isset($e[1])) {
  500. $e[1] = ltrim($e[1]);
  501. }
  502. if (
  503. ($e[0] == 'CONTENT_DISPOSITION')
  504. && isset($e[1])
  505. ) {
  506. parse_str(strtr($e[1], HTTPRequest::$hvaltr), $this->mpartcondisp);
  507. if (!isset($this->mpartcondisp['form-data'])) {
  508. break;
  509. }
  510. if (!isset($this->mpartcondisp['name'])) {
  511. break;
  512. }
  513. $this->mpartcondisp['name'] = trim($this->mpartcondisp['name'], '"');
  514. if (isset($this->mpartcondisp['filename'])) {
  515. $this->mpartcondisp['filename'] = trim($this->mpartcondisp['filename'], '"');
  516. if (!ini_get('file_uploads')) {
  517. break;
  518. }
  519. $this->attrs->files[$this->mpartcondisp['name']] = array(
  520. 'name' => $this->mpartcondisp['filename'],
  521. 'type' => '',
  522. 'tmp_name' => '',
  523. 'error' => UPLOAD_ERR_OK,
  524. 'size' => 0,
  525. );
  526. $tmpdir = ini_get('upload_tmp_dir');
  527. if ($tmpdir === false) {
  528. $this->attrs->files[$this->mpartcondisp['name']]['fp'] = false;
  529. $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_NO_TMP_DIR;
  530. } else {
  531. $this->attrs->files[$this->mpartcondisp['name']]['fp'] = @fopen($this->attrs->files[$this->mpartcondisp['name']]['tmp_name'] = tempnam($tmpdir, 'php'), 'w');
  532. if (!$this->attrs->files[$this->mpartcondisp['name']]['fp']) {
  533. $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_CANT_WRITE;
  534. }
  535. }
  536. $this->mpartstate = 3;
  537. } else {
  538. $this->attrs->post[$this->mpartcondisp['name']] = '';
  539. }
  540. }
  541. elseif (
  542. ($e[0] == 'CONTENT_TYPE')
  543. && isset($e[1])
  544. ) {
  545. if (
  546. isset($this->mpartcondisp['name'])
  547. && isset($this->mpartcondisp['filename'])
  548. ) {
  549. $this->attrs->files[$this->mpartcondisp['name']]['type'] = $e[1];
  550. }
  551. }
  552. }
  553. if ($this->mpartstate === 1) {
  554. $this->mpartstate = 2;
  555. }
  556. $continue = true;
  557. }
  558. }
  559. elseif (
  560. ($this->mpartstate === 2)
  561. || ($this->mpartstate === 3)
  562. ) {
  563. // process the body
  564. if (
  565. (($p = strpos($this->attrs->stdinbuf, $ndl = "\r\n--" . $this->boundary . "\r\n", $this->mpartoffset)) !== false)
  566. || (($p = strpos($this->attrs->stdinbuf, $ndl = "\r\n--" . $this->boundary . "--\r\n", $this->mpartoffset)) !== false)
  567. ) {
  568. if (
  569. ($this->mpartstate === 2)
  570. && isset($this->mpartcondisp['name'])
  571. ) {
  572. $this->attrs->post[$this->mpartcondisp['name']] .= binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p-$this->mpartoffset);
  573. }
  574. elseif (
  575. ($this->mpartstate === 3)
  576. && isset($this->mpartcondisp['filename'])
  577. ) {
  578. if ($this->attrs->files[$this->mpartcondisp['name']]['fp']) {
  579. fwrite($this->attrs->files[$this->mpartcondisp['name']]['fp'], binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p-$this->mpartoffset));
  580. }
  581. $this->attrs->files[$this->mpartcondisp['name']]['size'] += $p-$this->mpartoffset;
  582. }
  583. if ($ndl === "\r\n--" . $this->boundary . "--\r\n") {
  584. $this->mpartoffset = $p+strlen($ndl);
  585. $this->mpartstate = 0; // we done at all
  586. } else {
  587. $this->mpartoffset = $p;
  588. $this->mpartstate = 1; // let us parse the next part
  589. $continue = true;
  590. }
  591. $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset);
  592. $this->mpartoffset = 0;
  593. } else {
  594. $p = strrpos($this->attrs->stdinbuf, "\r\n", $this->mpartoffset);
  595. if ($p !== false) {
  596. if (
  597. ($this->mpartstate === 2)
  598. && isset($this->mpartcondisp['name'])
  599. ) {
  600. $this->attrs->post[$this->mpartcondisp['name']] .= binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset);
  601. }
  602. elseif (
  603. ($this->mpartstate === 3)
  604. && isset($this->mpartcondisp['filename'])
  605. ) {
  606. if ($this->attrs->files[$this->mpartcondisp['name']]['fp']) {
  607. fwrite($this->attrs->files[$this->mpartcondisp['name']]['fp'], binarySubstr($this->attrs->stdinbuf, $this->mpartoffset, $p - $this->mpartoffset));
  608. }
  609. $this->attrs->files[$this->mpartcondisp['name']]['size'] += $p - $this->mpartoffset;
  610. if ($this->parseSize(ini_get('upload_max_filesize')) < $this->attrs->files[$this->mpartcondisp['name']]['size']) {
  611. $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_INI_SIZE;
  612. }
  613. if (
  614. isset($this->attrs->post['MAX_FILE_SIZE'])
  615. && ($this->attrs->post['MAX_FILE_SIZE'] < $this->attrs->files[$this->mpartcondisp['name']]['size'])
  616. ) {
  617. $this->attrs->files[$this->mpartcondisp['name']]['error'] = UPLOAD_ERR_FORM_SIZE;
  618. }
  619. }
  620. $this->mpartoffset = $p;
  621. $this->attrs->stdinbuf = binarySubstr($this->attrs->stdinbuf, $this->mpartoffset);
  622. $this->mpartoffset = 0;
  623. }
  624. }
  625. }
  626. } while ($continue);
  627. }
  628. /**
  629. * Tells whether the file was uploaded via HTTP POST
  630. * @param string The filename being checked.
  631. * @return void
  632. */
  633. public function isUploadedFile($filename) {
  634. if (strpos($filename,ini_get('upload_tmp_dir').'/') !== 0) {
  635. return false;
  636. }
  637. foreach ($this->attrs->files as $file) {
  638. if ($file['tmp_name'] === $filename) {
  639. goto found;
  640. }
  641. }
  642. return false;
  643. found:
  644. return file_exists($file['tmp_name']);
  645. }
  646. /**
  647. * Moves an uploaded file to a new location
  648. * @param string The filename of the uploaded file.
  649. * @param string The destination of the moved file.
  650. * @return void
  651. */
  652. public function moveUploadedFile($filename,$dest) {
  653. if (!$this->isUploadedFile($filename)) {
  654. return false;
  655. }
  656. return rename($filename,$dest);
  657. }
  658. /**
  659. * Read request body from the file given in REQUEST_BODY_FILE parameter.
  660. * @return void
  661. */
  662. public function readBodyFile() {
  663. if (!isset($this->attrs->server['REQUEST_BODY_FILE'])) {
  664. return false;
  665. }
  666. $fp = fopen($this->attrs->server['REQUEST_BODY_FILE'], 'rb');
  667. if (!$fp) {
  668. Daemon::log('Couldn\'t open request-body file \'' . $this->attrs->server['REQUEST_BODY_FILE'] . '\' (REQUEST_BODY_FILE).');
  669. return false;
  670. }
  671. while (!feof($fp)) {
  672. $this->stdin($this->fread($fp, 4096));
  673. }
  674. fclose($fp);
  675. $this->attrs->stdin_done = true;
  676. }
  677. /**
  678. * Replacement for default parse_str(), it supoorts UCS-2 like this: %uXXXX
  679. * @param string String to parse.
  680. * @param array Reference to the resulting array.
  681. * @return void
  682. */
  683. public static function parse_str($s, &$array) {
  684. static $cb;
  685. if ($cb === NULL) {
  686. $cb = function ($m) {
  687. return urlencode(html_entity_decode('&#' . hexdec($m[1]) . ';', ENT_NOQUOTES, 'utf-8'));
  688. };
  689. }
  690. if (
  691. (stripos($s,'%u') !== false)
  692. && preg_match('~(%u[a-f\d]{4}|%[c-f][a-f\d](?!%[89a-f][a-f\d]))~is', $s, $m)
  693. ) {
  694. $s = preg_replace_callback('~%(u[a-f\d]{4}|[a-f\d]{2})~i', $cb, $s);
  695. }
  696. parse_str($s, $array);
  697. }
  698. /**
  699. * @todo description missing
  700. * @todo protected?
  701. */
  702. public function postFinishHandler() {
  703. if (!$this->headers_sent) {
  704. $this->out('');
  705. }
  706. if ($this->sendfp) {
  707. fclose($this->sendfp);
  708. }
  709. if (isset($this->attrs->files)) {
  710. foreach ($this->attrs->files as &$f) {
  711. if (
  712. ($f['error'] === UPLOAD_ERR_OK)
  713. && file_exists($f['tmp_name'])
  714. ) {
  715. unlink($f['tmp_name']);
  716. }
  717. }
  718. }
  719. if (isset($this->attrs->session)) {
  720. $this->sessionCommit();
  721. }
  722. }
  723. public function sessionCommit() {
  724. session_commit();
  725. }
  726. }