1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37:
38: namespace MOC\Core;
39: use MOC\Api;
40: use MOC\Generic\Device\Core;
41:
42: 43: 44:
45: class Proxy implements Core {
46:
47: private static $Singleton = null;
48:
49: 50: 51: 52: 53: 54:
55: public static function InterfaceInstance() {
56: if( self::$Singleton === null ) {
57: self::$Singleton = new Proxy();
58: } return self::$Singleton;
59: }
60:
61: 62: 63: 64: 65: 66:
67: public static function InterfaceChangelog() {
68: return Api::Core()->Changelog()->Create( __CLASS__ );
69: }
70:
71: 72: 73: 74: 75: 76:
77: public static function InterfaceDepending() {
78: return Api::Core()->Depending();
79: }
80:
81: const PROXY_NONE = 0;
82: const PROXY_RELAY = 1;
83: const PROXY_BASIC = 2;
84:
85:
86: private $Server = null;
87:
88: private $Credentials = null;
89:
90: private $Timeout = 5;
91: private $ErrorNumber = null;
92: private $ErrorString = null;
93: private $ProxyType = self::PROXY_NONE;
94:
95: 96: 97: 98:
99: public function Url( Drive\File $File ) {
100: return Proxy\Seo::InterfaceInstance()->Url( $File );
101: }
102:
103: 104: 105: 106: 107: 108:
109: public function Open( Proxy\Server $Server, Proxy\Credentials $Credentials = null ) {
110:
111: $this->Server = $Server;
112: $this->Credentials = $Credentials;
113:
114: if( $this->Credentials == null ) {
115: $this->ProxyType = self::PROXY_RELAY;
116: } else {
117: $this->ProxyType = self::PROXY_BASIC;
118: }
119:
120: return $this;
121: }
122:
123: 124: 125:
126: public function Close() {
127:
128: $this->Server = null;
129: $this->Credentials = null;
130:
131: $this->ProxyType = self::PROXY_NONE;
132:
133: return $this;
134: }
135:
136: 137: 138:
139: public function Server() {
140: return Proxy\Server::InterfaceInstance();
141: }
142:
143: 144: 145:
146: public function Credentials() {
147: return Proxy\Credentials::InterfaceInstance();
148: }
149: 150: 151: 152: 153: 154: 155:
156: public function GetFile( $Url, $Status = false ) {
157: switch( $this->IsProxy() ) {
158: case self::PROXY_NONE: return $this->ProxyNone( $Url, $Status );
159: case self::PROXY_RELAY: return $this->ProxyRelay( $Url, $Status );
160: case self::PROXY_BASIC: return $this->ProxyBasic( $Url, $Status );
161: default: throw new \Exception('Proxy not available!');
162: }
163: }
164:
165: 166: 167: 168: 169:
170: public function GetContent( $Url ) {
171: return $this->cURL( $Url );
172: }
173:
174: 175: 176:
177: private function IsProxy() {
178: if( $this->ProxyType !== self::PROXY_NONE ) {
179: return $this->ProxyType;
180: } else {
181: return false;
182: }
183: }
184:
185: 186: 187: 188: 189: 190:
191: private function ProxyNone( $Url, $Status ) {
192: $this->Server = Proxy\Server::InterfaceInstance();
193: $this->Server->Host( parse_url( $Url, PHP_URL_HOST ) );
194: if( parse_url( $Url, PHP_URL_PORT ) === null ) {
195: switch( strtoupper( parse_url( $Url, PHP_URL_SCHEME ) ) ) {
196: case 'HTTP': { $this->Server->Port( '80' ); break; }
197: case 'HTTPS': { $this->Server->Port( '443' ); break; }
198: }
199: } else {
200: $this->Server->Port( parse_url( $Url, PHP_URL_PORT ) );
201: }
202: if( $this->Server->Port() == '443' ) {
203: return file_get_contents( $Url );
204: }
205: if( ($Socket = fsockopen( $this->Server->Host(), $this->Server->Port(), $this->ErrorNumber, $this->ErrorString, $this->Timeout )) ) {
206: $Content = '';
207: fputs( $Socket, "GET ".$Url." HTTP/1.0\r\nHost: ".parse_url( $Url, PHP_URL_HOST )."\r\n\r\n");
208: while( !feof( $Socket ) ) {
209: $Content .= fread( $Socket, 4096 );
210: if( $Status ) {
211: $Match = array();
212: preg_match( '![0-9]{3}!', $Content, $Match );
213: return $Match[0];
214: }
215: }
216:
217: $ContentToCheck = $this->StatusCode( $Content, $Url );
218: fclose( $Socket );
219: if( $Content == $ContentToCheck ) {
220:
221: $Header = substr( $Content, 0, strpos( $Content, "\r\n\r\n" ) +4 );
222: $Content = substr( $Content, strpos( $Content, "\r\n\r\n" ) +4 );
223: if( preg_match( '!content-encoding: gzip!is', $Header ) ) {
224: $Content = $this->gzdecode( $Content );
225: }
226: } else {
227:
228: $Content = $ContentToCheck;
229: }
230: } else {
231: trigger_error( '['.$this->ErrorNumber.'] '.$this->ErrorString );
232: $Content = null;
233: }
234: return $Content;
235: }
236:
237: 238: 239: 240: 241: 242:
243: private function ProxyRelay( $Url, $Status ) {
244: if( ($Socket = fsockopen( $this->Server->Host(), $this->Server->Port(), $this->ErrorNumber, $this->ErrorString, $this->Timeout )) ) {
245: $Content = '';
246: fputs( $Socket, "GET ".$Url." HTTP/1.0\r\nHost: ".$this->Server->Host()."\r\n\r\n");
247: while( !feof( $Socket ) ) {
248: $Content .= fread( $Socket, 4096 );
249: if( $Status ) {
250: $Match = array();
251: preg_match( '![0-9]{3}!', $Content, $Match );
252: return $Match[0];
253: }
254: }
255:
256: $ContentToCheck = $this->StatusCode( $Content );
257: fclose( $Socket );
258: if( $Content == $ContentToCheck ) {
259:
260: $Header = substr( $Content, 0, strpos( $Content, "\r\n\r\n" ) +4 );
261: $Content = substr( $Content, strpos( $Content, "\r\n\r\n" ) +4 );
262: if( preg_match( '!content-encoding: gzip!is', $Header ) ) {
263: $Content = $this->gzdecode( $Content );
264: }
265: } else {
266:
267: $Content = $ContentToCheck;
268: }
269: } else {
270: trigger_error( '['.$this->ErrorNumber.'] '.$this->ErrorString );
271: $Content = null;
272: }
273: return $Content;
274: }
275:
276: 277: 278: 279: 280: 281:
282: private function ProxyBasic( $Url, $Status ) {
283: if( ($Socket = fsockopen( $this->Server->Host(), $this->Server->Port(), $this->ErrorNumber, $this->ErrorString, $this->Timeout )) ) {
284: $Content = '';
285: fputs( $Socket, "GET ".$Url." HTTP/1.0\r\nHost: ".$this->Server->Host()."\r\n");
286: fputs( $Socket, "Proxy-Authorization: Basic ".base64_encode( $this->Credentials->Username().':'.$this->Credentials->Password() ) . "\r\n\r\n");
287: while( !feof( $Socket ) ) {
288: $Content .= fread( $Socket, 4096 );
289: if( $Status ) {
290: $Match = array();
291: preg_match( '![0-9]{3}!', $Content, $Match );
292: return $Match[0];
293: }
294: }
295:
296: $ContentToCheck = $this->StatusCode( $Content );
297: fclose( $Socket );
298: if( $Content == $ContentToCheck ) {
299:
300: $Header = substr( $Content, 0, strpos( $Content, "\r\n\r\n" ) +4 );
301: $Content = substr( $Content, strpos( $Content, "\r\n\r\n" ) +4 );
302: if( preg_match( '!content-encoding: gzip!is', $Header ) ) {
303: $Content = $this->gzdecode( $Content );
304: }
305: } else {
306:
307: $Content = $ContentToCheck;
308: }
309: } else {
310: trigger_error( '['.$this->ErrorNumber.'] '.$this->ErrorString );
311: $Content = null;
312: }
313: return $Content;
314: }
315:
316: 317: 318: 319: 320: 321:
322: private function StatusCode( $Content, $Url = null ) {
323: preg_match( '![0-9]{3}!', $Content, $Match );
324: switch( $Match[0] ) {
325: case '302': {
326: preg_match( '!(?<=Location: )([^\s\n]+)!', $Content, $Match );
327: if( parse_url( $Match[0] ) ) {
328:
329: if( null !== $Url ) {
330: $Match[0] = str_replace( $Url, '', $Match[0] ).'?'.parse_url( $Url, PHP_URL_QUERY );
331: }
332: $Content = $this->GetFile( $Match[0] );
333: }
334: return $Content;
335: break;
336: }
337: case '301': {
338: preg_match( '!(?<=Location: )([^\s\n]+)!', $Content, $Match );
339: if( parse_url( $Match[0] ) ) {
340: $Content = $this->GetFile( $Match[0] );
341: }
342: return $Content;
343: break;
344: }
345: case '200': {
346: return $Content;
347: }
348: default: {
349: trigger_error( __CLASS__.': Status-Code '.$Match[0] );
350: return $Content;
351: }
352: }
353: }
354:
355: private static $cURL = null;
356:
357: 358: 359: 360: 361:
362: private function cURL( $Url ) {
363: self::$cURL = curl_init();
364: curl_setopt ( self::$cURL, CURLOPT_URL, $Url );
365: curl_setopt ( self::$cURL, CURLOPT_HEADER, 0 );
366: ob_start();
367: curl_exec ( self::$cURL );
368: curl_close ( self::$cURL );
369: $Content = ob_get_contents();
370:
371: $Header = substr( $Content, 0, strpos( $Content, "\r\n\r\n" ) +4 );
372: $Content = substr( $Content, strpos( $Content, "\r\n\r\n" ) +4 );
373: if( preg_match( '!content-encoding: gzip!is', $Header ) ) {
374: $Content = $this->gzdecode( $Content );
375: }
376: ob_end_clean();
377: return $Content;
378: }
379:
380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390:
391: private function gzdecode($data,&$filename='',&$error='',$maxlength=null)
392: {
393: $len = strlen($data);
394: if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
395: $error = "Not in GZIP format.";
396: return null;
397: }
398: $method = ord(substr($data,2,1));
399: $flags = ord(substr($data,3,1));
400: if ($flags & 31 != $flags) {
401: $error = "Reserved bits not allowed.";
402: return null;
403: }
404:
405: $mtime = unpack("V", substr($data,4,4));
406: $mtime = $mtime[1];
407: $xfl = substr($data,8,1);
408: $os = substr($data,8,1);
409: $headerlen = 10;
410: $extralen = 0;
411: $extra = "";
412: if ($flags & 4) {
413:
414: if ($len - $headerlen - 2 < 8) {
415: return false;
416: }
417: $extralen = unpack("v",substr($data,8,2));
418: $extralen = $extralen[1];
419: if ($len - $headerlen - 2 - $extralen < 8) {
420: return false;
421: }
422: $extra = substr($data,10,$extralen);
423: $headerlen += 2 + $extralen;
424: }
425: $filenamelen = 0;
426: $filename = "";
427: if ($flags & 8) {
428:
429: if ($len - $headerlen - 1 < 8) {
430: return false;
431: }
432: $filenamelen = strpos(substr($data,$headerlen),chr(0));
433: if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) {
434: return false;
435: }
436: $filename = substr($data,$headerlen,$filenamelen);
437: $headerlen += $filenamelen + 1;
438: }
439: $commentlen = 0;
440: $comment = "";
441: if ($flags & 16) {
442:
443: if ($len - $headerlen - 1 < 8) {
444: return false;
445: }
446: $commentlen = strpos(substr($data,$headerlen),chr(0));
447: if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) {
448: return false;
449: }
450: $comment = substr($data,$headerlen,$commentlen);
451: $headerlen += $commentlen + 1;
452: }
453: $headercrc = "";
454: if ($flags & 2) {
455:
456: if ($len - $headerlen - 2 < 8) {
457: return false;
458: }
459: $calccrc = crc32(substr($data,0,$headerlen)) & 0xffff;
460: $headercrc = unpack("v", substr($data,$headerlen,2));
461: $headercrc = $headercrc[1];
462: if ($headercrc != $calccrc) {
463: $error = "Header checksum failed.";
464: return false;
465: }
466: $headerlen += 2;
467: }
468:
469: $datacrc = unpack("V",substr($data,-8,4));
470: $datacrc = sprintf('%u',$datacrc[1] & 0xFFFFFFFF);
471: $isize = unpack("V",substr($data,-4));
472: $isize = $isize[1];
473:
474: $bodylen = $len-$headerlen-8;
475: if ($bodylen < 1) {
476:
477: return null;
478: }
479: $body = substr($data,$headerlen,$bodylen);
480: $data = "";
481: if ($bodylen > 0) {
482: switch ($method) {
483: case 8:
484:
485: $data = gzinflate($body,$maxlength);
486: break;
487: default:
488: $error = "Unknown compression method.";
489: return false;
490: }
491: }
492:
493: $crc = sprintf("%u",crc32($data));
494: $crcOK = $crc == $datacrc;
495: $lenOK = $isize == strlen($data);
496: if (!$lenOK || !$crcOK) {
497: $error = ( $lenOK ? '' : 'Length check FAILED. ') . ( $crcOK ? '' : 'Checksum FAILED.');
498: return false;
499: }
500: return $data;
501: }
502:
503: 504: 505:
506: function __toString() {
507: return '';
508: }
509: }
510: