]> git.donarmstrong.com Git - roundcube.git/blob - program/lib/Net/Socket.php
c47eea8daeeb41ffeca46fbcebdeaa029dc28c5b
[roundcube.git] / program / lib / Net / Socket.php
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.0 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: Stig Bakken <ssb@php.net>                                   |
17 // |          Chuck Hagenbuch <chuck@horde.org>                           |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id: Socket.php 17 2005-10-03 20:25:31Z roundcube $
21 //
22
23 require_once 'PEAR.php';
24
25 /**
26  * Generalized Socket class. More docs to be written.
27  *
28  * @version 1.0
29  * @author Stig Bakken <ssb@php.net>
30  * @author Chuck Hagenbuch <chuck@horde.org>
31  */
32 class Net_Socket extends PEAR {
33     // {{{ properties
34
35     /** Socket file pointer. */
36     var $fp = null;
37
38     /** Whether the socket is blocking. */
39     var $blocking = true;
40
41     /** Whether the socket is persistent. */
42     var $persistent = false;
43
44     /** The IP address to connect to. */
45     var $addr = '';
46
47     /** The port number to connect to. */
48     var $port = 0;
49
50     /** Number of seconds to wait on socket connections before
51         assuming there's no more data. */
52     var $timeout = false;
53
54     /** Number of bytes to read at a time in readLine() and
55         readAll(). */
56     var $lineLength = 2048;
57     // }}}
58
59     // {{{ constructor
60     /**
61      * Constructs a new Net_Socket object.
62      *
63      * @access public
64      */
65     function Net_Socket()
66     {
67         $this->PEAR();
68     }
69     // }}}
70
71     // {{{ connect()
72     /**
73      * Connect to the specified port. If called when the socket is
74      * already connected, it disconnects and connects again.
75      *
76      * @param $addr string IP address or host name
77      * @param $port int TCP port number
78      * @param $persistent bool (optional) whether the connection is
79      *        persistent (kept open between requests by the web server)
80      * @param $timeout int (optional) how long to wait for data
81      * @param $options array see options for stream_context_create
82      * @access public
83      * @return mixed true on success or error object
84      */
85     function connect($addr, $port, $persistent = null, $timeout = null, $options = null)
86     {
87         if (is_resource($this->fp)) {
88             @fclose($this->fp);
89             $this->fp = null;
90         }
91
92         if (strspn($addr, '.0123456789') == strlen($addr)) {
93             $this->addr = $addr;
94         } else {
95             $this->addr = gethostbyname($addr);
96         }
97         $this->port = $port % 65536;
98         if ($persistent !== null) {
99             $this->persistent = $persistent;
100         }
101         if ($timeout !== null) {
102             $this->timeout = $timeout;
103         }
104         $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
105         $errno = 0;
106         $errstr = '';
107         if ($options && function_exists('stream_context_create')) {
108             if ($this->timeout) {
109                 $timeout = $this->timeout;
110             } else {
111                 $timeout = 0;
112             }
113             $context = stream_context_create($options);
114             $fp = $openfunc($this->addr, $this->port, $errno, $errstr, $timeout, $context);
115         } else {
116             if ($this->timeout) {
117                 $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $this->timeout);
118             } else {
119                 $fp = @$openfunc($this->addr, $this->port, $errno, $errstr);
120             }
121         }
122
123         if (!$fp) {
124             return $this->raiseError($errstr, $errno);
125         }
126
127         $this->fp = $fp;
128
129         return $this->setBlocking($this->blocking);
130     }
131     // }}}
132
133     // {{{ disconnect()
134     /**
135      * Disconnects from the peer, closes the socket.
136      *
137      * @access public
138      * @return mixed true on success or an error object otherwise
139      */
140     function disconnect()
141     {
142         if (is_resource($this->fp)) {
143             fclose($this->fp);
144             $this->fp = null;
145             return true;
146         }
147         return $this->raiseError("not connected");
148     }
149     // }}}
150
151     // {{{ isBlocking()
152     /**
153      * Find out if the socket is in blocking mode.
154      *
155      * @access public
156      * @return bool the current blocking mode.
157      */
158     function isBlocking()
159     {
160         return $this->blocking;
161     }
162     // }}}
163
164     // {{{ setBlocking()
165     /**
166      * Sets whether the socket connection should be blocking or
167      * not. A read call to a non-blocking socket will return immediately
168      * if there is no data available, whereas it will block until there
169      * is data for blocking sockets.
170      *
171      * @param $mode bool true for blocking sockets, false for nonblocking
172      * @access public
173      * @return mixed true on success or an error object otherwise
174      */
175     function setBlocking($mode)
176     {
177         if (is_resource($this->fp)) {
178             $this->blocking = $mode;
179             socket_set_blocking($this->fp, $this->blocking);
180             return true;
181         }
182         return $this->raiseError("not connected");
183     }
184     // }}}
185
186     // {{{ setTimeout()
187     /**
188      * Sets the timeout value on socket descriptor,
189      * expressed in the sum of seconds and microseconds
190      *
191      * @param $seconds int seconds
192      * @param $microseconds int microseconds
193      * @access public
194      * @return mixed true on success or an error object otherwise
195      */
196     function setTimeout($seconds, $microseconds)
197     {
198         if (is_resource($this->fp)) {
199             socket_set_timeout($this->fp, $seconds, $microseconds);
200             return true;
201         }
202         return $this->raiseError("not connected");
203     }
204     // }}}
205
206     // {{{ getStatus()
207     /**
208      * Returns information about an existing socket resource.
209      * Currently returns four entries in the result array:
210      *
211      * <p>
212      * timed_out (bool) - The socket timed out waiting for data<br>
213      * blocked (bool) - The socket was blocked<br>
214      * eof (bool) - Indicates EOF event<br>
215      * unread_bytes (int) - Number of bytes left in the socket buffer<br>
216      * </p>
217      *
218      * @access public
219      * @return mixed Array containing information about existing socket resource or an error object otherwise
220      */
221     function getStatus()
222     {
223         if (is_resource($this->fp)) {
224             return socket_get_status($this->fp);
225         }
226         return $this->raiseError("not connected");
227     }
228     // }}}
229
230     // {{{ gets()
231     /**
232      * Get a specified line of data
233      *
234      * @access public
235      * @return $size bytes of data from the socket, or a PEAR_Error if
236      *         not connected.
237      */
238     function gets($size)
239     {
240         if (is_resource($this->fp)) {
241             return fgets($this->fp, $size);
242         }
243         return $this->raiseError("not connected");
244     }
245     // }}}
246
247     // {{{ read()
248     /**
249      * Read a specified amount of data. This is guaranteed to return,
250      * and has the added benefit of getting everything in one fread()
251      * chunk; if you know the size of the data you're getting
252      * beforehand, this is definitely the way to go.
253      *
254      * @param $size The number of bytes to read from the socket.
255      * @access public
256      * @return $size bytes of data from the socket, or a PEAR_Error if
257      *         not connected.
258      */
259     function read($size)
260     {
261         if (is_resource($this->fp)) {
262             return fread($this->fp, $size);
263         }
264         return $this->raiseError("not connected");
265     }
266     // }}}
267
268     // {{{ write()
269     /**
270      * Write a specified amount of data.
271      *
272      * @access public
273      * @return mixed true on success or an error object otherwise
274      */
275     function write($data)
276     {
277         if (is_resource($this->fp)) {
278             return fwrite($this->fp, $data);
279         }
280         return $this->raiseError("not connected");
281     }
282     // }}}
283
284     // {{{ writeLine()
285     /**
286      * Write a line of data to the socket, followed by a trailing "\r\n".
287      *
288      * @access public
289      * @return mixed fputs result, or an error
290      */
291     function writeLine ($data)
292     {
293         if (is_resource($this->fp)) {
294             return $this->write($data . "\r\n");
295         }
296         return $this->raiseError("not connected");
297     }
298     // }}}
299
300     // {{{ eof()
301     /**
302      * Tests for end-of-file on a socket descriptor
303      *
304      * @access public
305      * @return bool
306      */
307     function eof()
308     {
309         return (is_resource($this->fp) && feof($this->fp));
310     }
311     // }}}
312
313     // {{{ readByte()
314     /**
315      * Reads a byte of data
316      *
317      * @access public
318      * @return 1 byte of data from the socket, or a PEAR_Error if
319      *         not connected.
320      */
321     function readByte()
322     {
323         if (is_resource($this->fp)) {
324             return ord($this->read(1));
325         }
326         return $this->raiseError("not connected");
327     }
328     // }}}
329
330     // {{{ readWord()
331     /**
332      * Reads a word of data
333      *
334      * @access public
335      * @return 1 word of data from the socket, or a PEAR_Error if
336      *         not connected.
337      */
338     function readWord()
339     {
340         if (is_resource($this->fp)) {
341             $buf = $this->read(2);
342             return (ord($buf[0]) + (ord($buf[1]) << 8));
343         }
344         return $this->raiseError("not connected");
345     }
346     // }}}
347
348     // {{{ readInt()
349     /**
350      * Reads an int of data
351      *
352      * @access public
353      * @return 1 int of data from the socket, or a PEAR_Error if
354      *         not connected.
355      */
356     function readInt()
357     {
358         if (is_resource($this->fp)) {
359             $buf = $this->read(4);
360             return (ord($buf[0]) + (ord($buf[1]) << 8) +
361                     (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
362         }
363         return $this->raiseError("not connected");
364     }
365     // }}}
366
367     // {{{ readString()
368     /**
369      * Reads a zeroterminated string of data
370      *
371      * @access public
372      * @return string, or a PEAR_Error if
373      *         not connected.
374      */
375     function readString()
376     {
377         if (is_resource($this->fp)) {
378             $string = '';
379             while (($char = $this->read(1)) != "\x00")  {
380                 $string .= $char;
381             }
382             return $string;
383         }
384         return $this->raiseError("not connected");
385     }
386     // }}}
387
388     // {{{ readIPAddress()
389     /**
390      * Reads an IP Address and returns it in a dot formated string
391      *
392      * @access public
393      * @return Dot formated string, or a PEAR_Error if
394      *         not connected.
395      */
396     function readIPAddress()
397     {
398         if (is_resource($this->fp)) {
399             $buf = $this->read(4);
400             return sprintf("%s.%s.%s.%s", ord($buf[0]), ord($buf[1]),
401                            ord($buf[2]), ord($buf[3]));
402         }
403         return $this->raiseError("not connected");
404     }
405     // }}}
406
407     // {{{ readLine()
408     /**
409      * Read until either the end of the socket or a newline, whichever
410      * comes first. Strips the trailing newline from the returned data.
411      *
412      * @access public
413      * @return All available data up to a newline, without that
414      *         newline, or until the end of the socket, or a PEAR_Error if
415      *         not connected.
416      */
417     function readLine()
418     {
419         if (is_resource($this->fp)) {
420             $line = '';
421             $timeout = time() + $this->timeout;
422             while (!$this->eof() && (!$this->timeout || time() < $timeout)) {
423                 $line .= $this->gets($this->lineLength);
424                 if (substr($line, -2) == "\r\n" ||
425                     substr($line, -1) == "\n") {
426                     return rtrim($line, "\r\n");
427                 }
428             }
429             return $line;
430         }
431         return $this->raiseError("not connected");
432     }
433     // }}}
434
435     // {{{ readAll()
436     /**
437      * Read until the socket closes. THIS FUNCTION WILL NOT EXIT if the
438      * socket is in blocking mode until the socket closes.
439      *
440      * @access public
441      * @return All data until the socket closes, or a PEAR_Error if
442      *         not connected.
443      */
444     function readAll()
445     {
446         if (is_resource($this->fp)) {
447             $data = '';
448             while (!$this->eof())
449                 $data .= $this->read($this->lineLength);
450             return $data;
451         }
452         return $this->raiseError("not connected");
453     }
454     // }}}
455
456 }