]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/Net/Socket.php
[ROUTES] Allow accept-header specification during router creation
[quix0rs-gnu-social.git] / extlib / Net / Socket.php
1 <?php
2 /**
3  * Net_Socket
4  *
5  * PHP Version 5
6  *
7  * LICENSE:
8  *
9  * Copyright (c) 1997-2017 The PHP Group
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  *
16  * o Redistributions of source code must retain the above copyright
17  *   notice, this list of conditions and the following disclaimer.
18  * o Redistributions in binary form must reproduce the above copyright
19  *   notice, this list of conditions and the following disclaimer in the
20  *   documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * @category  Net
35  * @package   Net_Socket
36  * @author    Stig Bakken <ssb@php.net>
37  * @author    Chuck Hagenbuch <chuck@horde.org>
38  * @copyright 1997-2017 The PHP Group
39  * @license   http://opensource.org/licenses/bsd-license.php BSD-2-Clause
40  * @link      http://pear.php.net/packages/Net_Socket
41  */
42
43 require_once 'PEAR.php';
44
45 define('NET_SOCKET_READ', 1);
46 define('NET_SOCKET_WRITE', 2);
47 define('NET_SOCKET_ERROR', 4);
48
49 /**
50  * Generalized Socket class.
51  *
52  * @category  Net
53  * @package   Net_Socket
54  * @author    Stig Bakken <ssb@php.net>
55  * @author    Chuck Hagenbuch <chuck@horde.org>
56  * @copyright 1997-2017 The PHP Group
57  * @license   http://opensource.org/licenses/bsd-license.php BSD-2-Clause
58  * @link      http://pear.php.net/packages/Net_Socket
59  */
60 class Net_Socket extends PEAR
61 {
62     /**
63      * Socket file pointer.
64      * @var resource $fp
65      */
66     public $fp = null;
67
68     /**
69      * Whether the socket is blocking. Defaults to true.
70      * @var boolean $blocking
71      */
72     public $blocking = true;
73
74     /**
75      * Whether the socket is persistent. Defaults to false.
76      * @var boolean $persistent
77      */
78     public $persistent = false;
79
80     /**
81      * The IP address to connect to.
82      * @var string $addr
83      */
84     public $addr = '';
85
86     /**
87      * The port number to connect to.
88      * @var integer $port
89      */
90     public $port = 0;
91
92     /**
93      * Number of seconds to wait on socket operations before assuming
94      * there's no more data. Defaults to no timeout.
95      * @var integer|float $timeout
96      */
97     public $timeout = null;
98
99     /**
100      * Number of bytes to read at a time in readLine() and
101      * readAll(). Defaults to 2048.
102      * @var integer $lineLength
103      */
104     public $lineLength = 2048;
105
106     /**
107      * The string to use as a newline terminator. Usually "\r\n" or "\n".
108      * @var string $newline
109      */
110     public $newline = "\r\n";
111
112     /**
113      * Connect to the specified port. If called when the socket is
114      * already connected, it disconnects and connects again.
115      *
116      * @param string $addr IP address or host name (may be with protocol prefix).
117      * @param integer $port TCP port number.
118      * @param boolean $persistent (optional) Whether the connection is
119      *                            persistent (kept open between requests
120      *                            by the web server).
121      * @param integer $timeout (optional) Connection socket timeout.
122      * @param array $options See options for stream_context_create.
123      *
124      * @access public
125      *
126      * @return boolean|PEAR_Error  True on success or a PEAR_Error on failure.
127      */
128     public function connect(
129         $addr,
130         $port = 0,
131         $persistent = null,
132         $timeout = null,
133         $options = null
134     ) {
135         if (is_resource($this->fp)) {
136             @fclose($this->fp);
137             $this->fp = null;
138         }
139
140         if (!$addr) {
141             return $this->raiseError('$addr cannot be empty');
142         } else {
143             if (strspn($addr, ':.0123456789') === strlen($addr)) {
144                 $this->addr = strpos($addr, ':') !== false ? '[' . $addr . ']' : $addr;
145             } else {
146                 $this->addr = $addr;
147             }
148         }
149
150         $this->port = $port % 65536;
151
152         if ($persistent !== null) {
153             $this->persistent = $persistent;
154         }
155
156         $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
157         $errno = 0;
158         $errstr = '';
159
160         if (function_exists('error_clear_last')) {
161             error_clear_last();
162         } else {
163             $old_track_errors = @ini_set('track_errors', 1);
164         }
165
166         if ($timeout <= 0) {
167             $timeout = @ini_get('default_socket_timeout');
168         }
169
170         if ($options && function_exists('stream_context_create')) {
171             $context = stream_context_create($options);
172
173             // Since PHP 5 fsockopen doesn't allow context specification
174             if (function_exists('stream_socket_client')) {
175                 $flags = STREAM_CLIENT_CONNECT;
176
177                 if ($this->persistent) {
178                     $flags = STREAM_CLIENT_PERSISTENT;
179                 }
180
181                 $addr = $this->addr . ':' . $this->port;
182                 $fp = @stream_socket_client($addr, $errno, $errstr,
183                     $timeout, $flags, $context);
184             } else {
185                 $fp = @$openfunc($this->addr, $this->port, $errno,
186                     $errstr, $timeout, $context);
187             }
188         } else {
189             $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout);
190         }
191
192         if (!$fp) {
193             if ($errno === 0 && !strlen($errstr)) {
194                 $errstr = '';
195                 if (isset($old_track_errors)) {
196                     $errstr = $php_errormsg ?: '';  
197                     @ini_set('track_errors', $old_track_errors);
198                 } else {
199                     $lastError = error_get_last();
200                     if (isset($lastError['message'])) {
201                         $errstr = $lastError['message'];
202                     }
203                 }
204             }
205
206             return $this->raiseError($errstr, $errno);
207         }
208
209         if (isset($old_track_errors)) {
210             @ini_set('track_errors', $old_track_errors);
211         }
212
213         $this->fp = $fp;
214         $this->setTimeout();
215
216         return $this->setBlocking($this->blocking);
217     }
218
219     /**
220      * Disconnects from the peer, closes the socket.
221      *
222      * @access public
223      * @return mixed true on success or a PEAR_Error instance otherwise
224      */
225     public function disconnect()
226     {
227         if (!is_resource($this->fp)) {
228             return $this->raiseError('not connected');
229         }
230
231         @fclose($this->fp);
232         $this->fp = null;
233
234         return true;
235     }
236
237     /**
238      * Set the newline character/sequence to use.
239      *
240      * @param string $newline Newline character(s)
241      * @return boolean True
242      */
243     public function setNewline($newline)
244     {
245         $this->newline = $newline;
246
247         return true;
248     }
249
250     /**
251      * Find out if the socket is in blocking mode.
252      *
253      * @access public
254      * @return boolean  The current blocking mode.
255      */
256     public function isBlocking()
257     {
258         return $this->blocking;
259     }
260
261     /**
262      * Sets whether the socket connection should be blocking or
263      * not. A read call to a non-blocking socket will return immediately
264      * if there is no data available, whereas it will block until there
265      * is data for blocking sockets.
266      *
267      * @param boolean $mode True for blocking sockets, false for nonblocking.
268      *
269      * @access public
270      * @return mixed true on success or a PEAR_Error instance otherwise
271      */
272     public function setBlocking($mode)
273     {
274         if (!is_resource($this->fp)) {
275             return $this->raiseError('not connected');
276         }
277
278         $this->blocking = $mode;
279         stream_set_blocking($this->fp, (int)$this->blocking);
280
281         return true;
282     }
283
284     /**
285      * Sets the timeout value on socket descriptor,
286      * expressed in the sum of seconds and microseconds
287      *
288      * @param integer $seconds Seconds.
289      * @param integer $microseconds Microseconds, optional.
290      *
291      * @access public
292      * @return mixed True on success or false on failure or
293      *               a PEAR_Error instance when not connected
294      */
295     public function setTimeout($seconds = null, $microseconds = null)
296     {
297         if (!is_resource($this->fp)) {
298             return $this->raiseError('not connected');
299         }
300
301         if ($seconds === null && $microseconds === null) {
302             $seconds = (int)$this->timeout;
303             $microseconds = (int)(($this->timeout - $seconds) * 1000000);
304         } else {
305             $this->timeout = $seconds + $microseconds / 1000000;
306         }
307
308         if ($this->timeout > 0) {
309             return stream_set_timeout($this->fp, (int)$seconds, (int)$microseconds);
310         } else {
311             return false;
312         }
313     }
314
315     /**
316      * Sets the file buffering size on the stream.
317      * See php's stream_set_write_buffer for more information.
318      *
319      * @param integer $size Write buffer size.
320      *
321      * @access public
322      * @return mixed on success or an PEAR_Error object otherwise
323      */
324     public function setWriteBuffer($size)
325     {
326         if (!is_resource($this->fp)) {
327             return $this->raiseError('not connected');
328         }
329
330         $returned = stream_set_write_buffer($this->fp, $size);
331         if ($returned === 0) {
332             return true;
333         }
334
335         return $this->raiseError('Cannot set write buffer.');
336     }
337
338     /**
339      * Returns information about an existing socket resource.
340      * Currently returns four entries in the result array:
341      *
342      * <p>
343      * timed_out (bool) - The socket timed out waiting for data<br>
344      * blocked (bool) - The socket was blocked<br>
345      * eof (bool) - Indicates EOF event<br>
346      * unread_bytes (int) - Number of bytes left in the socket buffer<br>
347      * </p>
348      *
349      * @access public
350      * @return mixed Array containing information about existing socket
351      *               resource or a PEAR_Error instance otherwise
352      */
353     public function getStatus()
354     {
355         if (!is_resource($this->fp)) {
356             return $this->raiseError('not connected');
357         }
358
359         return stream_get_meta_data($this->fp);
360     }
361
362     /**
363      * Get a specified line of data
364      *
365      * @param int $size Reading ends when size - 1 bytes have been read,
366      *                  or a newline or an EOF (whichever comes first).
367      *                  If no size is specified, it will keep reading from
368      *                  the stream until it reaches the end of the line.
369      *
370      * @access public
371      * @return mixed $size bytes of data from the socket, or a PEAR_Error if
372      *         not connected. If an error occurs, FALSE is returned.
373      */
374     public function gets($size = null)
375     {
376         if (!is_resource($this->fp)) {
377             return $this->raiseError('not connected');
378         }
379
380         if (null === $size) {
381             return @fgets($this->fp);
382         } else {
383             return @fgets($this->fp, $size);
384         }
385     }
386
387     /**
388      * Read a specified amount of data. This is guaranteed to return,
389      * and has the added benefit of getting everything in one fread()
390      * chunk; if you know the size of the data you're getting
391      * beforehand, this is definitely the way to go.
392      *
393      * @param integer $size The number of bytes to read from the socket.
394      *
395      * @access public
396      * @return string $size bytes of data from the socket, or a PEAR_Error if
397      *         not connected.
398      */
399     public function read($size)
400     {
401         if (!is_resource($this->fp)) {
402             return $this->raiseError('not connected');
403         }
404
405         return @fread($this->fp, $size);
406     }
407
408     /**
409      * Write a specified amount of data.
410      *
411      * @param string $data Data to write.
412      * @param integer $blocksize Amount of data to write at once.
413      *                           NULL means all at once.
414      *
415      * @access public
416      * @return mixed If the socket is not connected, returns an instance of
417      *               PEAR_Error.
418      *               If the write succeeds, returns the number of bytes written.
419      *               If the write fails, returns false.
420      *               If the socket times out, returns an instance of PEAR_Error.
421      */
422     public function write($data, $blocksize = null)
423     {
424         if (!is_resource($this->fp)) {
425             return $this->raiseError('not connected');
426         }
427
428         if (null === $blocksize && !OS_WINDOWS) {
429             $written = @fwrite($this->fp, $data);
430
431             // Check for timeout or lost connection
432             if ($written === false) {
433                 $meta_data = $this->getStatus();
434
435                 if (!is_array($meta_data)) {
436                     return $meta_data; // PEAR_Error
437                 }
438
439                 if (!empty($meta_data['timed_out'])) {
440                     return $this->raiseError('timed out');
441                 }
442             }
443
444             return $written;
445         } else {
446             if (null === $blocksize) {
447                 $blocksize = 1024;
448             }
449
450             $pos = 0;
451             $size = strlen($data);
452             while ($pos < $size) {
453                 $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
454
455                 // Check for timeout or lost connection
456                 if ($written === false) {
457                     $meta_data = $this->getStatus();
458
459                     if (!is_array($meta_data)) {
460                         return $meta_data; // PEAR_Error
461                     }
462
463                     if (!empty($meta_data['timed_out'])) {
464                         return $this->raiseError('timed out');
465                     }
466
467                     return $written;
468                 }
469
470                 $pos += $written;
471             }
472
473             return $pos;
474         }
475     }
476
477     /**
478      * Write a line of data to the socket, followed by a trailing newline.
479      *
480      * @param string $data Data to write
481      *
482      * @access public
483      * @return mixed fwrite() result, or PEAR_Error when not connected
484      */
485     public function writeLine($data)
486     {
487         if (!is_resource($this->fp)) {
488             return $this->raiseError('not connected');
489         }
490
491         return fwrite($this->fp, $data . $this->newline);
492     }
493
494     /**
495      * Tests for end-of-file on a socket descriptor.
496      *
497      * Also returns true if the socket is disconnected.
498      *
499      * @access public
500      * @return bool
501      */
502     public function eof()
503     {
504         return (!is_resource($this->fp) || feof($this->fp));
505     }
506
507     /**
508      * Reads a byte of data
509      *
510      * @access public
511      * @return integer 1 byte of data from the socket, or a PEAR_Error if
512      *         not connected.
513      */
514     public function readByte()
515     {
516         if (!is_resource($this->fp)) {
517             return $this->raiseError('not connected');
518         }
519
520         return ord(@fread($this->fp, 1));
521     }
522
523     /**
524      * Reads a word of data
525      *
526      * @access public
527      * @return integer 1 word of data from the socket, or a PEAR_Error if
528      *         not connected.
529      */
530     public function readWord()
531     {
532         if (!is_resource($this->fp)) {
533             return $this->raiseError('not connected');
534         }
535
536         $buf = @fread($this->fp, 2);
537
538         return (ord($buf[0]) + (ord($buf[1]) << 8));
539     }
540
541     /**
542      * Reads an int of data
543      *
544      * @access public
545      * @return integer  1 int of data from the socket, or a PEAR_Error if
546      *                  not connected.
547      */
548     public function readInt()
549     {
550         if (!is_resource($this->fp)) {
551             return $this->raiseError('not connected');
552         }
553
554         $buf = @fread($this->fp, 4);
555
556         return (ord($buf[0]) + (ord($buf[1]) << 8) +
557             (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
558     }
559
560     /**
561      * Reads a zero-terminated string of data
562      *
563      * @access public
564      * @return string, or a PEAR_Error if
565      *         not connected.
566      */
567     public function readString()
568     {
569         if (!is_resource($this->fp)) {
570             return $this->raiseError('not connected');
571         }
572
573         $string = '';
574         while (($char = @fread($this->fp, 1)) !== "\x00") {
575             $string .= $char;
576         }
577
578         return $string;
579     }
580
581     /**
582      * Reads an IP Address and returns it in a dot formatted string
583      *
584      * @access public
585      * @return string Dot formatted string, or a PEAR_Error if
586      *         not connected.
587      */
588     public function readIPAddress()
589     {
590         if (!is_resource($this->fp)) {
591             return $this->raiseError('not connected');
592         }
593
594         $buf = @fread($this->fp, 4);
595
596         return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
597             ord($buf[2]), ord($buf[3]));
598     }
599
600     /**
601      * Read until either the end of the socket or a newline, whichever
602      * comes first. Strips the trailing newline from the returned data.
603      *
604      * @access public
605      * @return string All available data up to a newline, without that
606      *         newline, or until the end of the socket, or a PEAR_Error if
607      *         not connected.
608      */
609     public function readLine()
610     {
611         if (!is_resource($this->fp)) {
612             return $this->raiseError('not connected');
613         }
614
615         $line = '';
616
617         $timeout = time() + $this->timeout;
618
619         while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
620             $line .= @fgets($this->fp, $this->lineLength);
621             if (substr($line, -1) == "\n") {
622                 return rtrim($line, $this->newline);
623             }
624         }
625
626         return $line;
627     }
628
629     /**
630      * Read until the socket closes, or until there is no more data in
631      * the inner PHP buffer. If the inner buffer is empty, in blocking
632      * mode we wait for at least 1 byte of data. Therefore, in
633      * blocking mode, if there is no data at all to be read, this
634      * function will never exit (unless the socket is closed on the
635      * remote end).
636      *
637      * @access public
638      *
639      * @return string  All data until the socket closes, or a PEAR_Error if
640      *                 not connected.
641      */
642     public function readAll()
643     {
644         if (!is_resource($this->fp)) {
645             return $this->raiseError('not connected');
646         }
647
648         $data = '';
649         $timeout = time() + $this->timeout;
650
651         while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
652             $data .= @fread($this->fp, $this->lineLength);
653         }
654
655         return $data;
656     }
657
658     /**
659      * Runs the equivalent of the select() system call on the socket
660      * with a timeout specified by tv_sec and tv_usec.
661      *
662      * @param integer $state Which of read/write/error to check for.
663      * @param integer $tv_sec Number of seconds for timeout.
664      * @param integer $tv_usec Number of microseconds for timeout.
665      *
666      * @access public
667      * @return False if select fails, integer describing which of read/write/error
668      *         are ready, or PEAR_Error if not connected.
669      */
670     public function select($state, $tv_sec, $tv_usec = 0)
671     {
672         if (!is_resource($this->fp)) {
673             return $this->raiseError('not connected');
674         }
675
676         $read = null;
677         $write = null;
678         $except = null;
679         if ($state & NET_SOCKET_READ) {
680             $read[] = $this->fp;
681         }
682         if ($state & NET_SOCKET_WRITE) {
683             $write[] = $this->fp;
684         }
685         if ($state & NET_SOCKET_ERROR) {
686             $except[] = $this->fp;
687         }
688         if (false === ($sr = stream_select($read, $write, $except,
689                 $tv_sec, $tv_usec))
690         ) {
691             return false;
692         }
693
694         $result = 0;
695         if (count($read)) {
696             $result |= NET_SOCKET_READ;
697         }
698         if (count($write)) {
699             $result |= NET_SOCKET_WRITE;
700         }
701         if (count($except)) {
702             $result |= NET_SOCKET_ERROR;
703         }
704
705         return $result;
706     }
707
708     /**
709      * Turns encryption on/off on a connected socket.
710      *
711      * @param bool $enabled Set this parameter to true to enable encryption
712      *                         and false to disable encryption.
713      * @param integer $type Type of encryption. See stream_socket_enable_crypto()
714      *                         for values.
715      *
716      * @see    http://se.php.net/manual/en/function.stream-socket-enable-crypto.php
717      * @access public
718      * @return false on error, true on success and 0 if there isn't enough data
719      *         and the user should try again (non-blocking sockets only).
720      *         A PEAR_Error object is returned if the socket is not
721      *         connected
722      */
723     public function enableCrypto($enabled, $type)
724     {
725         if (version_compare(phpversion(), '5.1.0', '>=')) {
726             if (!is_resource($this->fp)) {
727                 return $this->raiseError('not connected');
728             }
729
730             return @stream_socket_enable_crypto($this->fp, $enabled, $type);
731         } else {
732             $msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0';
733
734             return $this->raiseError($msg);
735         }
736     }
737
738 }