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: