1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20: App::import('Core', array('CakeSocket', 'Set', 'Router'));
21:
22: 23: 24: 25: 26: 27: 28: 29: 30:
31: class HttpSocket extends CakeSocket {
32:
33: 34: 35: 36: 37: 38:
39: var $description = 'HTTP-based DataSource Interface';
40:
41: 42: 43: 44: 45: 46: 47: 48:
49: var $quirksMode = false;
50:
51: 52: 53: 54: 55: 56:
57: var $request = array(
58: 'method' => 'GET',
59: 'uri' => array(
60: 'scheme' => 'http',
61: 'host' => null,
62: 'port' => 80,
63: 'user' => null,
64: 'pass' => null,
65: 'path' => null,
66: 'query' => null,
67: 'fragment' => null
68: ),
69: 'auth' => array(
70: 'method' => 'Basic',
71: 'user' => null,
72: 'pass' => null
73: ),
74: 'version' => '1.1',
75: 'body' => '',
76: 'line' => null,
77: 'header' => array(
78: 'Connection' => 'close',
79: 'User-Agent' => 'CakePHP'
80: ),
81: 'raw' => null,
82: 'cookies' => array()
83: );
84:
85: 86: 87: 88: 89: 90:
91: var $response = array(
92: 'raw' => array(
93: 'status-line' => null,
94: 'header' => null,
95: 'body' => null,
96: 'response' => null
97: ),
98: 'status' => array(
99: 'http-version' => null,
100: 'code' => null,
101: 'reason-phrase' => null
102: ),
103: 'header' => array(),
104: 'body' => '',
105: 'cookies' => array()
106: );
107:
108: 109: 110: 111: 112: 113:
114: var $config = array(
115: 'persistent' => false,
116: 'host' => 'localhost',
117: 'protocol' => 'tcp',
118: 'port' => 80,
119: 'timeout' => 30,
120: 'request' => array(
121: 'uri' => array(
122: 'scheme' => 'http',
123: 'host' => 'localhost',
124: 'port' => 80
125: ),
126: 'auth' => array(
127: 'method' => 'Basic',
128: 'user' => null,
129: 'pass' => null
130: ),
131: 'cookies' => array()
132: )
133: );
134:
135: 136: 137: 138: 139: 140:
141: var $lineBreak = "\r\n";
142:
143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164:
165: function __construct($config = array()) {
166: if (is_string($config)) {
167: $this->_configUri($config);
168: } elseif (is_array($config)) {
169: if (isset($config['request']['uri']) && is_string($config['request']['uri'])) {
170: $this->_configUri($config['request']['uri']);
171: unset($config['request']['uri']);
172: }
173: $this->config = Set::merge($this->config, $config);
174: }
175: parent::__construct($this->config);
176: }
177:
178: 179: 180: 181: 182: 183: 184: 185:
186: function request($request = array()) {
187: $this->reset(false);
188:
189: if (is_string($request)) {
190: $request = array('uri' => $request);
191: } elseif (!is_array($request)) {
192: return false;
193: }
194:
195: if (!isset($request['uri'])) {
196: $request['uri'] = null;
197: }
198: $uri = $this->_parseUri($request['uri']);
199: $hadAuth = false;
200: if (is_array($uri) && array_key_exists('user', $uri)) {
201: $hadAuth = true;
202: }
203: if (!isset($uri['host'])) {
204: $host = $this->config['host'];
205: }
206: if (isset($request['host'])) {
207: $host = $request['host'];
208: unset($request['host']);
209: }
210: $request['uri'] = $this->url($request['uri']);
211: $request['uri'] = $this->_parseUri($request['uri'], true);
212: $this->request = Set::merge($this->request, $this->config['request'], $request);
213:
214: if (!$hadAuth && !empty($this->config['request']['auth']['user'])) {
215: $this->request['uri']['user'] = $this->config['request']['auth']['user'];
216: $this->request['uri']['pass'] = $this->config['request']['auth']['pass'];
217: }
218: $this->_configUri($this->request['uri']);
219:
220: if (isset($host)) {
221: $this->config['host'] = $host;
222: }
223: $cookies = null;
224:
225: if (is_array($this->request['header'])) {
226: $this->request['header'] = $this->_parseHeader($this->request['header']);
227: if (!empty($this->request['cookies'])) {
228: $cookies = $this->buildCookies($this->request['cookies']);
229: }
230: $Host = $this->request['uri']['host'];
231: $schema = '';
232: $port = 0;
233: if (isset($this->request['uri']['schema'])) {
234: $schema = $this->request['uri']['schema'];
235: }
236: if (isset($this->request['uri']['port'])) {
237: $port = $this->request['uri']['port'];
238: }
239: if (
240: ($schema === 'http' && $port != 80) ||
241: ($schema === 'https' && $port != 443) ||
242: ($port != 80 && $port != 443)
243: ) {
244: $Host .= ':' . $port;
245: }
246: $this->request['header'] = array_merge(compact('Host'), $this->request['header']);
247: }
248:
249: if (isset($this->request['auth']['user']) && isset($this->request['auth']['pass'])) {
250: $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['auth']['user'] . ":" . $this->request['auth']['pass']);
251: }
252: if (isset($this->request['uri']['user']) && isset($this->request['uri']['pass'])) {
253: $this->request['header']['Authorization'] = $this->request['auth']['method'] . " " . base64_encode($this->request['uri']['user'] . ":" . $this->request['uri']['pass']);
254: }
255:
256: if (is_array($this->request['body'])) {
257: $this->request['body'] = $this->_httpSerialize($this->request['body']);
258: }
259:
260: if (!empty($this->request['body']) && !isset($this->request['header']['Content-Type'])) {
261: $this->request['header']['Content-Type'] = 'application/x-www-form-urlencoded';
262: }
263:
264: if (!empty($this->request['body']) && !isset($this->request['header']['Content-Length'])) {
265: $this->request['header']['Content-Length'] = strlen($this->request['body']);
266: }
267:
268: $connectionType = null;
269: if (isset($this->request['header']['Connection'])) {
270: $connectionType = $this->request['header']['Connection'];
271: }
272: $this->request['header'] = $this->_buildHeader($this->request['header']) . $cookies;
273:
274: if (empty($this->request['line'])) {
275: $this->request['line'] = $this->_buildRequestLine($this->request);
276: }
277:
278: if ($this->quirksMode === false && $this->request['line'] === false) {
279: return $this->response = false;
280: }
281:
282: if ($this->request['line'] !== false) {
283: $this->request['raw'] = $this->request['line'];
284: }
285:
286: if ($this->request['header'] !== false) {
287: $this->request['raw'] .= $this->request['header'];
288: }
289:
290: $this->request['raw'] .= "\r\n";
291: $this->request['raw'] .= $this->request['body'];
292: $this->write($this->request['raw']);
293:
294: $response = null;
295: while ($data = $this->read()) {
296: $response .= $data;
297: }
298:
299: if ($connectionType == 'close') {
300: $this->disconnect();
301: }
302:
303: $this->response = $this->_parseResponse($response);
304: if (!empty($this->response['cookies'])) {
305: $this->config['request']['cookies'] = array_merge($this->config['request']['cookies'], $this->response['cookies']);
306: }
307:
308: return $this->response['body'];
309: }
310:
311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334:
335: function get($uri = null, $query = array(), $request = array()) {
336: if (!empty($query)) {
337: $uri = $this->_parseUri($uri);
338: if (isset($uri['query'])) {
339: $uri['query'] = array_merge($uri['query'], $query);
340: } else {
341: $uri['query'] = $query;
342: }
343: $uri = $this->_buildUri($uri);
344: }
345:
346: $request = Set::merge(array('method' => 'GET', 'uri' => $uri), $request);
347: return $this->request($request);
348: }
349:
350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367:
368: function post($uri = null, $data = array(), $request = array()) {
369: $request = Set::merge(array('method' => 'POST', 'uri' => $uri, 'body' => $data), $request);
370: return $this->request($request);
371: }
372:
373: 374: 375: 376: 377: 378: 379: 380: 381:
382: function put($uri = null, $data = array(), $request = array()) {
383: $request = Set::merge(array('method' => 'PUT', 'uri' => $uri, 'body' => $data), $request);
384: return $this->request($request);
385: }
386:
387: 388: 389: 390: 391: 392: 393: 394: 395:
396: function delete($uri = null, $data = array(), $request = array()) {
397: $request = Set::merge(array('method' => 'DELETE', 'uri' => $uri, 'body' => $data), $request);
398: return $this->request($request);
399: }
400:
401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428:
429: function url($url = null, $uriTemplate = null) {
430: if (is_null($url)) {
431: $url = '/';
432: }
433: if (is_string($url)) {
434: if ($url{0} == '/') {
435: $url = $this->config['request']['uri']['host'].':'.$this->config['request']['uri']['port'] . $url;
436: }
437: if (!preg_match('/^.+:\/\/|\*|^\//', $url)) {
438: $url = $this->config['request']['uri']['scheme'].'://'.$url;
439: }
440: } elseif (!is_array($url) && !empty($url)) {
441: return false;
442: }
443:
444: $base = array_merge($this->config['request']['uri'], array('scheme' => array('http', 'https'), 'port' => array(80, 443)));
445: $url = $this->_parseUri($url, $base);
446:
447: if (empty($url)) {
448: $url = $this->config['request']['uri'];
449: }
450:
451: if (!empty($uriTemplate)) {
452: return $this->_buildUri($url, $uriTemplate);
453: }
454: return $this->_buildUri($url);
455: }
456:
457: 458: 459: 460: 461: 462: 463:
464: function _parseResponse($message) {
465: if (is_array($message)) {
466: return $message;
467: } elseif (!is_string($message)) {
468: return false;
469: }
470:
471: static $responseTemplate;
472:
473: if (empty($responseTemplate)) {
474: $classVars = get_class_vars(__CLASS__);
475: $responseTemplate = $classVars['response'];
476: }
477:
478: $response = $responseTemplate;
479:
480: if (!preg_match("/^(.+\r\n)(.*)(?<=\r\n)\r\n/Us", $message, $match)) {
481: return false;
482: }
483:
484: list($null, $response['raw']['status-line'], $response['raw']['header']) = $match;
485: $response['raw']['response'] = $message;
486: $response['raw']['body'] = substr($message, strlen($match[0]));
487:
488: if (preg_match("/(.+) ([0-9]{3}) (.+)\r\n/DU", $response['raw']['status-line'], $match)) {
489: $response['status']['http-version'] = $match[1];
490: $response['status']['code'] = (int)$match[2];
491: $response['status']['reason-phrase'] = $match[3];
492: }
493:
494: $response['header'] = $this->_parseHeader($response['raw']['header']);
495: $transferEncoding = null;
496: if (isset($response['header']['Transfer-Encoding'])) {
497: $transferEncoding = $response['header']['Transfer-Encoding'];
498: }
499: $decoded = $this->_decodeBody($response['raw']['body'], $transferEncoding);
500: $response['body'] = $decoded['body'];
501:
502: if (!empty($decoded['header'])) {
503: $response['header'] = $this->_parseHeader($this->_buildHeader($response['header']).$this->_buildHeader($decoded['header']));
504: }
505:
506: if (!empty($response['header'])) {
507: $response['cookies'] = $this->parseCookies($response['header']);
508: }
509:
510: foreach ($response['raw'] as $field => $val) {
511: if ($val === '') {
512: $response['raw'][$field] = null;
513: }
514: }
515:
516: return $response;
517: }
518:
519: 520: 521: 522: 523: 524: 525: 526: 527:
528: function _decodeBody($body, $encoding = 'chunked') {
529: if (!is_string($body)) {
530: return false;
531: }
532: if (empty($encoding)) {
533: return array('body' => $body, 'header' => false);
534: }
535: $decodeMethod = '_decode'.Inflector::camelize(str_replace('-', '_', $encoding)).'Body';
536:
537: if (!is_callable(array(&$this, $decodeMethod))) {
538: if (!$this->quirksMode) {
539: trigger_error(sprintf(__('HttpSocket::_decodeBody - Unknown encoding: %s. Activate quirks mode to surpress error.', true), h($encoding)), E_USER_WARNING);
540: }
541: return array('body' => $body, 'header' => false);
542: }
543: return $this->{$decodeMethod}($body);
544: }
545:
546: 547: 548: 549: 550: 551: 552: 553:
554: function _decodeChunkedBody($body) {
555: if (!is_string($body)) {
556: return false;
557: }
558:
559: $decodedBody = null;
560: $chunkLength = null;
561:
562: while ($chunkLength !== 0) {
563: if (!preg_match("/^([0-9a-f]+) *(?:;(.+)=(.+))?\r\n/iU", $body, $match)) {
564: if (!$this->quirksMode) {
565: trigger_error(__('HttpSocket::_decodeChunkedBody - Could not parse malformed chunk. Activate quirks mode to do this.', true), E_USER_WARNING);
566: return false;
567: }
568: break;
569: }
570:
571: $chunkSize = 0;
572: $hexLength = 0;
573: $chunkExtensionName = '';
574: $chunkExtensionValue = '';
575: if (isset($match[0])) {
576: $chunkSize = $match[0];
577: }
578: if (isset($match[1])) {
579: $hexLength = $match[1];
580: }
581: if (isset($match[2])) {
582: $chunkExtensionName = $match[2];
583: }
584: if (isset($match[3])) {
585: $chunkExtensionValue = $match[3];
586: }
587:
588: $body = substr($body, strlen($chunkSize));
589: $chunkLength = hexdec($hexLength);
590: $chunk = substr($body, 0, $chunkLength);
591: if (!empty($chunkExtensionName)) {
592: 593: 594:
595: }
596: $decodedBody .= $chunk;
597: if ($chunkLength !== 0) {
598: $body = substr($body, $chunkLength+strlen("\r\n"));
599: }
600: }
601:
602: $entityHeader = false;
603: if (!empty($body)) {
604: $entityHeader = $this->_parseHeader($body);
605: }
606: return array('body' => $decodedBody, 'header' => $entityHeader);
607: }
608:
609: 610: 611: 612: 613: 614: 615:
616: function _configUri($uri = null) {
617: if (empty($uri)) {
618: return false;
619: }
620:
621: if (is_array($uri)) {
622: $uri = $this->_parseUri($uri);
623: } else {
624: $uri = $this->_parseUri($uri, true);
625: }
626:
627: if (!isset($uri['host'])) {
628: return false;
629: }
630: $config = array(
631: 'request' => array(
632: 'uri' => array_intersect_key($uri, $this->config['request']['uri']),
633: 'auth' => array_intersect_key($uri, $this->config['request']['auth'])
634: )
635: );
636: $this->config = Set::merge($this->config, $config);
637: $this->config = Set::merge($this->config, array_intersect_key($this->config['request']['uri'], $this->config));
638: return $this->config;
639: }
640:
641: 642: 643: 644: 645: 646: 647: 648:
649: function _buildUri($uri = array(), $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment') {
650: if (is_string($uri)) {
651: $uri = array('host' => $uri);
652: }
653: $uri = $this->_parseUri($uri, true);
654:
655: if (!is_array($uri) || empty($uri)) {
656: return false;
657: }
658:
659: $uri['path'] = preg_replace('/^\//', null, $uri['path']);
660: $uri['query'] = $this->_httpSerialize($uri['query']);
661: $stripIfEmpty = array(
662: 'query' => '?%query',
663: 'fragment' => '#%fragment',
664: 'user' => '%user:%pass@',
665: 'host' => '%host:%port/'
666: );
667:
668: foreach ($stripIfEmpty as $key => $strip) {
669: if (empty($uri[$key])) {
670: $uriTemplate = str_replace($strip, null, $uriTemplate);
671: }
672: }
673:
674: $defaultPorts = array('http' => 80, 'https' => 443);
675: if (array_key_exists($uri['scheme'], $defaultPorts) && $defaultPorts[$uri['scheme']] == $uri['port']) {
676: $uriTemplate = str_replace(':%port', null, $uriTemplate);
677: }
678: foreach ($uri as $property => $value) {
679: $uriTemplate = str_replace('%'.$property, $value, $uriTemplate);
680: }
681:
682: if ($uriTemplate === '/*') {
683: $uriTemplate = '*';
684: }
685: return $uriTemplate;
686: }
687:
688: 689: 690: 691: 692: 693: 694: 695: 696:
697: function _parseUri($uri = null, $base = array()) {
698: $uriBase = array(
699: 'scheme' => array('http', 'https'),
700: 'host' => null,
701: 'port' => array(80, 443),
702: 'user' => null,
703: 'pass' => null,
704: 'path' => '/',
705: 'query' => null,
706: 'fragment' => null
707: );
708:
709: if (is_string($uri)) {
710: $uri = parse_url($uri);
711: }
712: if (!is_array($uri) || empty($uri)) {
713: return false;
714: }
715: if ($base === true) {
716: $base = $uriBase;
717: }
718:
719: if (isset($base['port'], $base['scheme']) && is_array($base['port']) && is_array($base['scheme'])) {
720: if (isset($uri['scheme']) && !isset($uri['port'])) {
721: $base['port'] = $base['port'][array_search($uri['scheme'], $base['scheme'])];
722: } elseif (isset($uri['port']) && !isset($uri['scheme'])) {
723: $base['scheme'] = $base['scheme'][array_search($uri['port'], $base['port'])];
724: }
725: }
726:
727: if (is_array($base) && !empty($base)) {
728: $uri = array_merge($base, $uri);
729: }
730:
731: if (isset($uri['scheme']) && is_array($uri['scheme'])) {
732: $uri['scheme'] = array_shift($uri['scheme']);
733: }
734: if (isset($uri['port']) && is_array($uri['port'])) {
735: $uri['port'] = array_shift($uri['port']);
736: }
737:
738: if (array_key_exists('query', $uri)) {
739: $uri['query'] = $this->_parseQuery($uri['query']);
740: }
741:
742: if (!array_intersect_key($uriBase, $uri)) {
743: return false;
744: }
745: return $uri;
746: }
747:
748: 749: 750: 751: 752: 753: 754: 755: 756: 757: 758: 759: 760: 761: 762:
763: function _parseQuery($query) {
764: if (is_array($query)) {
765: return $query;
766: }
767: $parsedQuery = array();
768:
769: if (is_string($query) && !empty($query)) {
770: $query = preg_replace('/^\?/', '', $query);
771: $items = explode('&', $query);
772:
773: foreach ($items as $item) {
774: if (strpos($item, '=') !== false) {
775: list($key, $value) = explode('=', $item, 2);
776: } else {
777: $key = $item;
778: $value = null;
779: }
780:
781: $key = urldecode($key);
782: $value = urldecode($value);
783:
784: if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) {
785: $subKeys = $matches[1];
786: $rootKey = substr($key, 0, strpos($key, '['));
787: if (!empty($rootKey)) {
788: array_unshift($subKeys, $rootKey);
789: }
790: $queryNode =& $parsedQuery;
791:
792: foreach ($subKeys as $subKey) {
793: if (!is_array($queryNode)) {
794: $queryNode = array();
795: }
796:
797: if ($subKey === '') {
798: $queryNode[] = array();
799: end($queryNode);
800: $subKey = key($queryNode);
801: }
802: $queryNode =& $queryNode[$subKey];
803: }
804: $queryNode = $value;
805: } else {
806: $parsedQuery[$key] = $value;
807: }
808: }
809: }
810: return $parsedQuery;
811: }
812:
813: 814: 815: 816: 817: 818: 819: 820:
821: function _buildRequestLine($request = array(), $versionToken = 'HTTP/1.1') {
822: $asteriskMethods = array('OPTIONS');
823:
824: if (is_string($request)) {
825: $isValid = preg_match("/(.+) (.+) (.+)\r\n/U", $request, $match);
826: if (!$this->quirksMode && (!$isValid || ($match[2] == '*' && !in_array($match[3], $asteriskMethods)))) {
827: trigger_error(__('HttpSocket::_buildRequestLine - Passed an invalid request line string. Activate quirks mode to do this.', true), E_USER_WARNING);
828: return false;
829: }
830: return $request;
831: } elseif (!is_array($request)) {
832: return false;
833: } elseif (!array_key_exists('uri', $request)) {
834: return false;
835: }
836:
837: $request['uri'] = $this->_parseUri($request['uri']);
838: $request = array_merge(array('method' => 'GET'), $request);
839: $request['uri'] = $this->_buildUri($request['uri'], '/%path?%query');
840:
841: if (!$this->quirksMode && $request['uri'] === '*' && !in_array($request['method'], $asteriskMethods)) {
842: trigger_error(sprintf(__('HttpSocket::_buildRequestLine - The "*" asterisk character is only allowed for the following methods: %s. Activate quirks mode to work outside of HTTP/1.1 specs.', true), join(',', $asteriskMethods)), E_USER_WARNING);
843: return false;
844: }
845: return $request['method'].' '.$request['uri'].' '.$versionToken.$this->lineBreak;
846: }
847:
848: 849: 850: 851: 852: 853: 854:
855: function _httpSerialize($data = array()) {
856: if (is_string($data)) {
857: return $data;
858: }
859: if (empty($data) || !is_array($data)) {
860: return false;
861: }
862: return substr(Router::queryString($data), 1);
863: }
864:
865: 866: 867: 868: 869: 870: 871:
872: function _buildHeader($header, $mode = 'standard') {
873: if (is_string($header)) {
874: return $header;
875: } elseif (!is_array($header)) {
876: return false;
877: }
878:
879: $returnHeader = '';
880: foreach ($header as $field => $contents) {
881: if (is_array($contents) && $mode == 'standard') {
882: $contents = implode(',', $contents);
883: }
884: foreach ((array)$contents as $content) {
885: $contents = preg_replace("/\r\n(?![\t ])/", "\r\n ", $content);
886: $field = $this->_escapeToken($field);
887:
888: $returnHeader .= $field.': '.$contents.$this->lineBreak;
889: }
890: }
891: return $returnHeader;
892: }
893:
894: 895: 896: 897: 898: 899: 900:
901: function _parseHeader($header) {
902: if (is_array($header)) {
903: foreach ($header as $field => $value) {
904: unset($header[$field]);
905: $field = strtolower($field);
906: preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE);
907:
908: foreach ($offsets[0] as $offset) {
909: $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1);
910: }
911: $header[$field] = $value;
912: }
913: return $header;
914: } elseif (!is_string($header)) {
915: return false;
916: }
917:
918: preg_match_all("/(.+):(.+)(?:(?<![\t ])" . $this->lineBreak . "|\$)/Uis", $header, $matches, PREG_SET_ORDER);
919:
920: $header = array();
921: foreach ($matches as $match) {
922: list(, $field, $value) = $match;
923:
924: $value = trim($value);
925: $value = preg_replace("/[\t ]\r\n/", "\r\n", $value);
926:
927: $field = $this->_unescapeToken($field);
928:
929: $field = strtolower($field);
930: preg_match_all('/(?:^|(?<=-))[a-z]/U', $field, $offsets, PREG_OFFSET_CAPTURE);
931: foreach ($offsets[0] as $offset) {
932: $field = substr_replace($field, strtoupper($offset[0]), $offset[1], 1);
933: }
934:
935: if (!isset($header[$field])) {
936: $header[$field] = $value;
937: } else {
938: $header[$field] = array_merge((array)$header[$field], (array)$value);
939: }
940: }
941: return $header;
942: }
943:
944: 945: 946: 947: 948: 949: 950: 951:
952: function parseCookies($header) {
953: if (!isset($header['Set-Cookie'])) {
954: return false;
955: }
956:
957: $cookies = array();
958: foreach ((array)$header['Set-Cookie'] as $cookie) {
959: if (strpos($cookie, '";"') !== false) {
960: $cookie = str_replace('";"', "{__cookie_replace__}", $cookie);
961: $parts = str_replace("{__cookie_replace__}", '";"', explode(';', $cookie));
962: } else {
963: $parts = preg_split('/\;[ \t]*/', $cookie);
964: }
965:
966: list($name, $value) = explode('=', array_shift($parts), 2);
967: $cookies[$name] = compact('value');
968:
969: foreach ($parts as $part) {
970: if (strpos($part, '=') !== false) {
971: list($key, $value) = explode('=', $part);
972: } else {
973: $key = $part;
974: $value = true;
975: }
976:
977: $key = strtolower($key);
978: if (!isset($cookies[$name][$key])) {
979: $cookies[$name][$key] = $value;
980: }
981: }
982: }
983: return $cookies;
984: }
985:
986: 987: 988: 989: 990: 991: 992: 993:
994: function buildCookies($cookies) {
995: $header = array();
996: foreach ($cookies as $name => $cookie) {
997: $header[] = $name.'='.$this->_escapeToken($cookie['value'], array(';'));
998: }
999: $header = $this->_buildHeader(array('Cookie' => implode('; ', $header)), 'pragmatic');
1000: return $header;
1001: }
1002:
1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010:
1011: function _unescapeToken($token, $chars = null) {
1012: $regex = '/"(['.join('', $this->_tokenEscapeChars(true, $chars)).'])"/';
1013: $token = preg_replace($regex, '\\1', $token);
1014: return $token;
1015: }
1016:
1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024:
1025: function _escapeToken($token, $chars = null) {
1026: $regex = '/(['.join('', $this->_tokenEscapeChars(true, $chars)).'])/';
1027: $token = preg_replace($regex, '"\\1"', $token);
1028: return $token;
1029: }
1030:
1031: 1032: 1033: 1034: 1035: 1036: 1037: 1038:
1039: function _tokenEscapeChars($hex = true, $chars = null) {
1040: if (!empty($chars)) {
1041: $escape = $chars;
1042: } else {
1043: $escape = array('"', "(", ")", "<", ">", "@", ",", ";", ":", "\\", "/", "[", "]", "?", "=", "{", "}", " ");
1044: for ($i = 0; $i <= 31; $i++) {
1045: $escape[] = chr($i);
1046: }
1047: $escape[] = chr(127);
1048: }
1049:
1050: if ($hex == false) {
1051: return $escape;
1052: }
1053: $regexChars = '';
1054: foreach ($escape as $key => $char) {
1055: $escape[$key] = '\\x'.str_pad(dechex(ord($char)), 2, '0', STR_PAD_LEFT);
1056: }
1057: return $escape;
1058: }
1059:
1060: 1061: 1062: 1063: 1064: 1065: 1066: 1067:
1068: function reset($full = true) {
1069: static $initalState = array();
1070: if (empty($initalState)) {
1071: $initalState = get_class_vars(__CLASS__);
1072: }
1073: if ($full == false) {
1074: $this->request = $initalState['request'];
1075: $this->response = $initalState['response'];
1076: return true;
1077: }
1078: parent::reset($initalState);
1079: return true;
1080: }
1081: }
1082: