Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
//
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1997-2003 The PHP Group                                |
7
// +----------------------------------------------------------------------+
8
// | This source file is subject to version 2.02 of the PHP license,      |
9
// | that is bundled with this package in the file LICENSE, and is        |
10
// | available at through the world-wide-web at                           |
11
// | http://www.php.net/license/2_02.txt.                                 |
12
// | If you did not receive a copy of the PHP license and are unable to   |
13
// | obtain it through the world-wide-web, please send a note to          |
14
// | license@php.net so we can mail you a copy immediately.               |
15
// +----------------------------------------------------------------------+
16
// | Authors: Shane Caraveo <Shane@Caraveo.com>                           |
17
// +----------------------------------------------------------------------+
18
//
19
// $Id: HTTP.php 696 2011-09-08 09:08:23Z tiefland $
20
//
21
/**
22
 * Needed Classes
23
 */
24
require_once 'PayPal/SOAP/Base.php';
25
require_once 'PayPal/SDKProxyProperties.php';
26
/**
27
 *  HTTP Transport for SOAP
28
 *
29
 * @access public
30
 * @package SOAP::Transport::HTTP
31
 * @author Shane Caraveo <shane@php.net>
32
 */
33
class SOAP_Transport_HTTP extends SOAP_Base
34
{
35
 
36
    /**
37
     * Basic Auth string
38
     *
39
     * @var  array
40
     */
41
    var $headers = array();
42
 
43
    /**
44
     * Cookies
45
     *
46
     * @var array
47
     */
48
    var $cookies;
49
 
50
    /**
51
     *
52
     * @var  int connection timeout in seconds - 0 = none
53
     */
54
    var $timeout = 300;
55
 
56
    /**
57
     * Array containing urlparts - parse_url()
58
     *
59
     * @var  mixed
60
     */
61
    var $urlparts = NULL;
62
 
63
    /**
64
     * Connection endpoint - URL
65
     *
66
     * @var  string
67
     */
68
    var $url = '';
69
 
70
    /**
71
     * Incoming payload
72
     *
73
     * @var  string
74
     */
75
    var $incoming_payload = '';
76
 
77
    /**
78
     * HTTP-Request User-Agent
79
     *
80
     * @var  string
81
     */
82
    var $_userAgent = SOAP_LIBRARY_NAME;
83
 
84
    /**
85
     * HTTP Encoding
86
     *
87
     * @var string
88
     */
89
    var $encoding = SOAP_DEFAULT_ENCODING;
90
 
91
    /**
92
     * HTTP-Response Content-Type encoding
93
     *
94
     * we assume UTF-8 if no encoding is set
95
     * @var  string
96
     */
97
    var $result_encoding = 'UTF-8';
98
 
99
    /**
100
     * HTTP-Response Content-Type
101
     */
102
    var $result_content_type;
103
 
104
    var $result_headers = array();
105
 
106
    var $result_cookies = array();
107
 
108
    /**
109
     * SOAP_Transport_HTTP Constructor
110
     *
111
     * @param string $URL    http url to soap endpoint
112
     *
113
     * @access public
114
     * @param  string $URI
115
     * @param  string $encoding  encoding to use
116
     */
117
    function SOAP_Transport_HTTP($URL, $encoding = SOAP_DEFAULT_ENCODING)
118
    {
119
        parent::SOAP_Base('HTTP');
120
        $this->urlparts = @parse_url($URL);
121
        $this->url = $URL;
122
        $this->encoding = $encoding;
123
    }
124
 
125
    /**
126
     * send and receive soap data
127
     *
128
     * @param string outgoing post data
129
     * @param array  options
130
     *
131
     * @return string|fault response
132
     * @access public
133
     */
134
    function &send(&$msg, $options = null)
135
    {
136
        if (!$this->_validateUrl()) {
137
            return $this->fault;
138
        }
139
 
140
        if (isset($options['timeout']))
141
            $this->timeout = (int)$options['timeout'];
142
 
143
        if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0) {
144
            return $this->_sendHTTP($msg, $options);
145
        } else if (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0) {
146
            return $this->_sendHTTPS($msg, $options);
147
        }
148
 
149
        return $this->_raiseSoapFault('Invalid url scheme '.$this->url);
150
    }
151
 
152
    /**
153
     * set data for http authentication
154
     * creates Authorization header
155
     *
156
     * @param string $username   username
157
     * @param string $password   response data, minus http headers
158
     *
159
     * @return none
160
     * @access public
161
     */
162
    function setCredentials($username, $password)
163
    {
164
        $this->headers['Authorization'] = 'Basic ' . base64_encode($username . ':' . $password);
165
    }
166
 
167
    /**
168
     * Add a cookie
169
     *
170
     * @access public
171
     * @param  string  $name   cookie name
172
     * @param  mixed   $value  cookie value
173
     * @return void
174
     */
175
    function addCookie($name, $value)
176
    {
177
        $this->cookies[$name]=$value;
178
    }
179
 
180
    // private methods
181
 
182
    /**
183
     * Generates the correct headers for the cookies
184
     *
185
     * @access private
186
     * @return void
187
     */
188
    function _genCookieHeader()
189
    {
190
        foreach ($this->cookies as $name=>$value) {
191
            $cookies = (isset($cookies) ? $cookies. '; ' : '') .
192
                        urlencode($name) . '=' . urlencode($value);
193
        }
194
        return $cookies;
195
    }
196
 
197
    /**
198
     * validate url data passed to constructor
199
     *
200
     * @access private
201
     * @return boolean
202
     */
203
    function _validateUrl()
204
    {
205
        if ( ! is_array($this->urlparts) ) {
206
            $this->_raiseSoapFault("Unable to parse URL " . $this->url);
207
            return false;
208
        }
209
        if (!isset($this->urlparts['host'])) {
210
            $this->_raiseSoapFault("No host in URL " . $this->url);
211
            return false;
212
        }
213
        if (!isset($this->urlparts['port'])) {
214
 
215
            if (strcasecmp($this->urlparts['scheme'], 'HTTP') == 0)
216
                $this->urlparts['port'] = 80;
217
            else if (strcasecmp($this->urlparts['scheme'], 'HTTPS') == 0)
218
                $this->urlparts['port'] = 443;
219
 
220
        }
221
        if (isset($this->urlparts['user'])) {
222
            $this->setCredentials(urldecode($this->urlparts['user']),
223
                                    urldecode($this->urlparts['pass']));
224
        }
225
        if (!isset($this->urlparts['path']) || !$this->urlparts['path'])
226
            $this->urlparts['path'] = '/';
227
        return true;
228
    }
229
 
230
    /**
231
     * Finds out what is the encoding.
232
     *
233
     * Sets the object property accordingly.
234
     *
235
     * @access private
236
     * @param  array $headers  headers
237
     * @return void
238
     */
239
    function _parseEncoding($headers)
240
    {
241
        $h = stristr($headers, 'Content-Type');
242
        preg_match_all('/^Content-Type:\s*(.*)$/im', $h, $ct, PREG_SET_ORDER);
243
        $n = count($ct);
244
        $ct = $ct[$n - 1];
245
        $this->result_content_type = str_replace("\r", '', $ct[1]);
246
        if (preg_match('/(.*?)(?:;\s?charset=)(.*)/i', $this->result_content_type, $m)) {
247
            // strip the string of \r
248
            $this->result_content_type = $m[1];
249
            if (count($m) > 2) {
250
                $enc = strtoupper(str_replace('"', '', $m[2]));
251
                if (in_array($enc, $this->_encodings)) {
252
                    $this->result_encoding = $enc;
253
                }
254
            }
255
        }
256
        // deal with broken servers that don't set content type on faults
257
        if (!$this->result_content_type) $this->result_content_type = 'text/xml';
258
    }
259
 
260
    /**
261
     * Parses the headers
262
     *
263
     * @param  array $headers the headers
264
     * @return void
265
     */
266
    function _parseHeaders($headers)
267
    {
268
        /* largely borrowed from HTTP_Request */
269
        $this->result_headers = array();
270
        $headers = split("\r?\n", $headers);
271
        foreach ($headers as $value) {
272
            if (strpos($value,':') === false) {
273
                $this->result_headers[0]=$value;
274
                continue;
275
            }
276
            list($name,$value) = split(':',$value);
277
            $headername = strtolower($name);
278
            $headervalue = trim($value);
279
            $this->result_headers[$headername]=$headervalue;
280
 
281
            if ($headername == 'set-cookie') {
282
                // Parse a SetCookie header to fill _cookies array
283
                $cookie = array(
284
                    'expires' => null,
285
                    'domain'  => $this->urlparts['host'],
286
                    'path'    => null,
287
                    'secure'  => false
288
                );
289
 
290
                // Only a name=value pair
291
                if (!strpos($headervalue, ';')) {
292
                    list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $headervalue));
293
                    $cookie['name']  = urldecode($cookie['name']);
294
                    $cookie['value'] = urldecode($cookie['value']);
295
 
296
                // Some optional parameters are supplied
297
                } else {
298
                    $elements = explode(';', $headervalue);
299
                    list($cookie['name'], $cookie['value']) = array_map('trim', explode('=', $elements[0]));
300
                    $cookie['name']  = urldecode($cookie['name']);
301
                    $cookie['value'] = urldecode($cookie['value']);
302
 
303
                    for ($i = 1; $i < count($elements);$i++) {
304
                        list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
305
                        if ('secure' == $elName) {
306
                            $cookie['secure'] = true;
307
                        } elseif ('expires' == $elName) {
308
                            $cookie['expires'] = str_replace('"', '', $elValue);
309
                        } elseif ('path' == $elName OR 'domain' == $elName) {
310
                            $cookie[$elName] = urldecode($elValue);
311
                        } else {
312
                            $cookie[$elName] = $elValue;
313
                        }
314
                    }
315
                }
316
                $this->result_cookies[] = $cookie;
317
            }
318
        }
319
    }
320
 
321
    /**
322
     * Remove http headers from response
323
     *
324
     * @return boolean
325
     * @access private
326
     */
327
    function _parseResponse()
328
    {
329
        if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $this->incoming_payload, $match)) {
330
            #$this->response = preg_replace("/[\r|\n]/", '', $match[2]);
331
 
332
// find the response error, some servers response with 500 for soap faults
333
            //Check if proxy headers are found
334
            //If found, ignore those headers
335
            if (preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $match[2], $match1))
336
            {
337
                if($match1[2] != '')
338
                {
339
                    $match[1] = $match1[1];
340
                    $match[2] = $match1[2];
341
                }
342
            }
343
 
344
            $this->response =& $match[2];
345
            // find the response error, some servers response with 500 for soap faults
346
            $this->_parseHeaders($match[1]);
347
 
348
            list($protocol, $code, $msg) = sscanf($this->result_headers[0], '%s %s %s');
349
            unset($this->result_headers[0]);
350
 
351
            switch($code) {
352
                case 100: // Continue
353
                   $this->incoming_payload = $match[2];
354
                   return $this->_parseResponse();
355
                case 400:
356
                    $this->_raiseSoapFault("HTTP Response $code Bad Request");
357
                    return false;
358
                    break;
359
                case 401:
360
                    $this->_raiseSoapFault("HTTP Response $code Authentication Failed");
361
                    return false;
362
                    break;
363
                case 403:
364
                    $this->_raiseSoapFault("HTTP Response $code Forbidden");
365
                    return false;
366
                    break;
367
                case 404:
368
                    $this->_raiseSoapFault("HTTP Response $code Not Found");
369
                    return false;
370
                    break;
371
                case 407:
372
                    $this->_raiseSoapFault("HTTP Response $code Proxy Authentication Required");
373
                    return false;
374
                    break;
375
                case 408:
376
                    $this->_raiseSoapFault("HTTP Response $code Request Timeout");
377
                    return false;
378
                    break;
379
                case 410:
380
                    $this->_raiseSoapFault("HTTP Response $code Gone");
381
                    return false;
382
                    break;
383
                default:
384
                    if ($code >= 400 && $code < 500) {
385
                        $this->_raiseSoapFault("HTTP Response $code Not Found, Server message: $msg");
386
                        return false;
387
                    }
388
            }
389
 
390
            $this->_parseEncoding($match[1]);
391
 
392
            if ($this->result_content_type == 'application/dime') {
393
                // XXX quick hack insertion of DIME
394
                if (PEAR::isError($this->_decodeDIMEMessage($this->response,$this->headers,$this->attachments))) {
395
                    // _decodeDIMEMessage already raised $this->fault
396
                    return false;
397
                }
398
                $this->result_content_type = $this->headers['content-type'];
399
            } else if (stristr($this->result_content_type,'multipart/related')) {
400
                $this->response = $this->incoming_payload;
401
                if (PEAR::isError($this->_decodeMimeMessage($this->response,$this->headers,$this->attachments))) {
402
                    // _decodeMimeMessage already raised $this->fault
403
                    return false;
404
                }
405
            } else if ($this->result_content_type != 'text/xml') {
406
                $this->_raiseSoapFault($this->response);
407
                return false;
408
            }
409
            // if no content, return false
410
            return strlen($this->response) > 0;
411
        }
412
        $this->_raiseSoapFault('Invalid HTTP Response');
413
        return false;
414
    }
415
 
416
    /**
417
     * Create http request, including headers, for outgoing request
418
     *
419
     * @param string   &$msg   outgoing SOAP package
420
     * @param $options
421
     * @return string outgoing_payload
422
     * @access private
423
     */
424
    function &_getRequest(&$msg, $options)
425
    {
426
        $action = isset($options['soapaction'])?$options['soapaction']:'';
427
        $fullpath = $this->urlparts['path'].
428
                        (isset($this->urlparts['query'])?'?'.$this->urlparts['query']:'').
429
                        (isset($this->urlparts['fragment'])?'#'.$this->urlparts['fragment']:'');
430
 
431
        if (isset($options['proxy_host'])) {
432
            $fullpath = 'http://' . $this->urlparts['host'] . ':' . $this->urlparts['port'] . $fullpath;
433
        }
434
 
435
        if (isset($options['proxy_user'])) {
436
            $this->headers['Proxy-Authorization'] = 'Basic ' . base64_encode($options['proxy_user'].":".$options['proxy_pass']);
437
        }
438
 
439
        if (isset($options['user'])) {
440
            $this->setCredentials($options['user'], $options['pass']);
441
        }
442
 
443
        $this->headers['User-Agent'] = $this->_userAgent;
444
        $this->headers['Host'] = $this->urlparts['host'];
445
        $this->headers['Content-Type'] = "text/xml; charset=$this->encoding";
446
        $this->headers['Content-Length'] = strlen($msg);
447
        $this->headers['SOAPAction'] = "\"$action\"";
448
        if (isset($options['headers'])) {
449
            $this->headers = array_merge($this->headers, $options['headers']);
450
        }
451
 
452
        $this->cookies = array();
453
        if (!isset($options['nocookies']) || !$options['nocookies']) {
454
            // add the cookies we got from the last request
455
            if (isset($this->result_cookies)) {
456
                foreach ($this->result_cookies as $cookie) {
457
                    if ($cookie['domain'] == $this->urlparts['host'])
458
                        $this->cookies[$cookie['name']]=$cookie['value'];
459
                }
460
            }
461
        }
462
        // add cookies the user wants to set
463
        if (isset($options['cookies'])) {
464
            foreach ($options['cookies'] as $cookie) {
465
                if ($cookie['domain'] == $this->urlparts['host'])
466
                    $this->cookies[$cookie['name']]=$cookie['value'];
467
            }
468
        }
469
        if (count($this->cookies)) {
470
            $this->headers['Cookie'] = $this->_genCookieHeader();
471
        }
472
        $headers = '';
473
        foreach ($this->headers as $k => $v) {
474
            $headers .= "$k: $v\r\n";
475
        }
476
        $this->outgoing_payload =
477
                "POST $fullpath HTTP/1.0\r\n".
478
                $headers."\r\n".
479
                $msg;
480
        return $this->outgoing_payload;
481
    }
482
 
483
    /**
484
     * Send outgoing request, and read/parse response
485
     *
486
     * @param string  &$msg   outgoing SOAP package
487
     * @param string  $action   SOAP Action
488
     * @return string &$response   response data, minus http headers
489
     * @access private
490
     */
491
    function &_sendHTTP(&$msg, $options)
492
    {
493
 
494
		$this->incoming_payload = '';
495
        $this->_getRequest($msg, $options);
496
        $host = $this->urlparts['host'];
497
        $port = $this->urlparts['port'];
498
        if (isset($options['proxy_host'])) {
499
            $host = $options['proxy_host'];
500
            $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
501
        }
502
        // send
503
        if ($this->timeout > 0) {
504
            $fp = @fsockopen($host, $port, $this->errno, $this->errmsg, $this->timeout);
505
        } else {
506
            $fp = @fsockopen($host, $port, $this->errno, $this->errmsg);
507
        }
508
        if (!$fp) {
509
            return $this->_raiseSoapFault("Connect Error to $host:$port");
510
        }
511
        if ($this->timeout > 0) {
512
            // some builds of php do not support this, silence
513
            // the warning
514
            @socket_set_timeout($fp, $this->timeout);
515
        }
516
        if (!fputs($fp, $this->outgoing_payload, strlen($this->outgoing_payload))) {
517
            return $this->_raiseSoapFault("Error POSTing Data to $host");
518
        }
519
 
520
        // get reponse
521
        // XXX time consumer
522
        do {
523
            $data = fread($fp, 4096);
524
            $_tmp_status = socket_get_status($fp);
525
            if ($_tmp_status['timed_out']) {
526
                return $this->_raiseSoapFault("Timed out read from $host");
527
            } else {
528
                $this->incoming_payload .= $data;
529
            }
530
        } while (!$_tmp_status['eof']);
531
 
532
        fclose($fp);
533
 
534
        if (!$this->_parseResponse()) {
535
            return $this->fault;
536
        }
537
        return $this->response;
538
    }
539
 
540
    /**
541
     * Send outgoing request, and read/parse response, via HTTPS
542
     *
543
     * @param string  &$msg   outgoing SOAP package
544
     * @param string  $action   SOAP Action
545
     * @return string &$response   response data, minus http headers
546
     * @access private
547
     */
548
    function &_sendHTTPS(&$msg, $options)
549
    {
550
        /* NOTE This function uses the CURL functions
551
         *  Your php must be compiled with CURL
552
         */
553
 
554
		//adding proxy supprt for HTTPS connections.
555
	   //if USE_PROXY set to TRUE in SDKProxyProperties.php(php-sdk\lib\PayPal),
556
	  //then only proxy is setted.
557
		if(USE_PROXY){
558
				$options['proxy_host'] = PROXY_HOST;
559
				$options['proxy_port']=  PROXY_PORT;
560
				$options['proxy_user'] = PROXY_USER;
561
				$options['proxy_pass']=  PROXY_PASSWORD;
562
		}
563
 
564
        if (!extension_loaded('curl')) {
565
            return $this->_raiseSoapFault('CURL Extension is required for HTTPS');
566
        }
567
 
568
        $ch = curl_init();
569
 
570
        if (isset($options['proxy_host'])) {
571
            // $options['http_proxy'] == 'hostname:port'
572
            $host = $options['proxy_host'];
573
            $port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
574
            curl_setopt($ch, CURLOPT_PROXY, $host . ":" . $port);
575
        }
576
 
577
        if (isset($options['proxy_user'])) {
578
            // $options['http_proxy_userpw'] == 'username:password'
579
            curl_setopt($ch, CURLOPT_PROXYUSERPWD, $options['proxy_user'] . ':' . $options['proxy_pass']);
580
        }
581
 
582
        if (isset($options['user'])) {
583
            curl_setopt($ch, CURLOPT_USERPWD, $options['user'] . ':' . $options['pass']);
584
        }
585
 
586
        if (!isset($options['soapaction'])) {
587
            $options['soapaction'] = '';
588
        }
589
        curl_setopt($ch, CURLOPT_HTTPHEADER ,    array('Content-Type: text/xml;charset=' . $this->encoding, 'SOAPAction: "'.$options['soapaction'].'"'));
590
        curl_setopt($ch, CURLOPT_USERAGENT ,     $this->_userAgent);
591
 
592
        if ($this->timeout) {
593
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); //times out after 4s
594
        }
595
 
596
        curl_setopt($ch, CURLOPT_POSTFIELDS,       $msg);
597
        curl_setopt($ch, CURLOPT_URL,              $this->url);
598
        curl_setopt($ch, CURLOPT_POST,             1);
599
        curl_setopt($ch, CURLOPT_FAILONERROR,      0);
600
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION,   1);
601
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,   1);
602
        curl_setopt($ch, CURLOPT_HEADER,           1);
603
        if (defined('CURLOPT_HTTP_VERSION')) {
604
            curl_setopt($ch, CURLOPT_HTTP_VERSION, 1);
605
        }
606
 
607
        if (isset($options['curl'])) {
608
            foreach ($options['curl'] as $key => $val) {
609
                curl_setopt($ch, $key, $val);
610
            }
611
        }
612
 
613
        // Save the outgoing XML. This doesn't quite match _sendHTTP
614
        // as CURL generates the headers, but having the XML is
615
        // usually the most important part for tracing/debugging.
616
        $this->outgoing_payload = $msg;
617
 
618
        $this->incoming_payload = curl_exec($ch);
619
        if (!$this->incoming_payload) {
620
            $m = 'curl_exec error ' . curl_errno($ch) . ' ' . curl_error($ch);
621
            curl_close($ch);
622
            return $this->_raiseSoapFault($m);
623
        }
624
        curl_close($ch);
625
 
626
        if (!$this->_parseResponse()) {
627
            return $this->fault;
628
        }
629
 
630
        return $this->response;
631
    }
632
 
633
}