PluginProbe
Postie / trunk
Postie vtrunk
1.9.80 1.9.79 1.9.78 1.9.77 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.10 1.7.11 1.7.12 1.7.13 1.7.14 1.7.15 1.7.16 1.7.17 1.7.18 1.7.2 1.7.20 All 239 releases
postie / lib / pSocketConnection.php

pSocketConnection.php in Postie trunk, at lib/pSocketConnection.php

282 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class pSocketConnection extends pConnection {
4
5 private $socket = null;
6 private $command_num = 1;
7
8 public function __construct($type, $host, $username, $password, $port = NULL, $secure = FALSE, $timeout = NULL) {
9 parent::__construct($type, $host, $username, $password, $port, $secure, $timeout);
10 DebugEcho("Connecting via Socket");
11 }
12
13 public function __destruct() {
14 $this->close();
15 }
16
17 public function close() {
18 if ($this->socket) {
19 fclose($this->socket);
20 $this->socket = null;
21 }
22 }
23
24 public function open() {
25 if ($this->socket) {
26 return;
27 }
28
29 // Use implicit TLS connection string prefix only if secure is requested and we are not on a plain-text port
30 $use_implicit_tls = false;
31 if ($this->secure) {
32 if ($this->type == 'imap') {
33 $use_implicit_tls = ($this->port != 143);
34 } elseif ($this->type == 'pop3') {
35 $use_implicit_tls = ($this->port != 110);
36 }
37 }
38
39 $connstr = ($use_implicit_tls ? 'tls://' : '') . "$this->host:$this->port";
40 DebugEcho("Socket: $connstr");
41 $error_number = 0;
42 $error_string = '';
43 $context = stream_context_create();
44 stream_context_set_option($context, "ssl", "allow_self_signed", true);
45 stream_context_set_option($context, "ssl", "verify_peer", false);
46 stream_context_set_option($context, "ssl", "verify_peer_name", false);
47 $this->socket = stream_socket_client($connstr, $error_number, $error_string, $this->timeout, STREAM_CLIENT_CONNECT, $context);
48 DebugEcho("Socket error: $error_number - $error_string ($this->socket)");
49
50 if (!$this->socket) {
51 throw new fConnectivityException('There was an error connecting to the server ' . $error_string);
52 }
53
54 stream_set_timeout($this->socket, $this->timeout);
55
56 $openSsl = extension_loaded('openssl');
57 if ($this->type == 'imap') {
58 DebugEcho("Socket: IMAP");
59 if ($this->secure && !$use_implicit_tls && $openSsl) {
60 $response = $this->write('CAPABILITY');
61 $supports_starttls = false;
62 foreach ($response as $line) {
63 if (preg_match('#\bstarttls\b#i', $line)) {
64 $supports_starttls = true;
65 break;
66 }
67 }
68 if ($supports_starttls) {
69 $this->write('STARTTLS');
70 do {
71 if (isset($res)) {
72 sleep(0.1);
73 }
74 $res = stream_socket_enable_crypto($this->socket, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
75 } while ($res === 0);
76 }
77 } else {
78 if (!$openSsl) {
79 DebugEcho("Socket: OpenSSL not enabled");
80 }
81 if ($this->secure && $use_implicit_tls) {
82 DebugEcho("Socket: Connected via Implicit TLS");
83 }
84 }
85
86 $response = $this->write('LOGIN ' . $this->username . ' "' . $this->password . '"');
87 if (!$response || !preg_match('#^[^ ]+\s+OK#', $response[count($response) - 1])) {
88 throw new fValidationException('IMAP - Could not connect to %1$s server %2$s on port %3$s. %4$s', strtoupper($this->type), $this->host, $this->port, print_r($response, true));
89 }
90 $this->write('SELECT "' . $this->mailbox . '"');
91 } elseif ($this->type == 'pop3') {
92 DebugEcho("Socket: POP3");
93 $response = $this->read(1);
94 if (isset($response[0])) {
95 if ($response[0][0] == '-') {
96 throw new fConnectivityException('There was an error connecting to the POP3 server %1$s on port %2$s', $this->host, $this->port);
97 }
98 preg_match('#<[^@]+@[^>]+>#', $response[0], $match);
99 }
100
101 if ($this->secure && !$use_implicit_tls && $openSsl) {
102 DebugEcho("Socket: attempting a secure connection");
103 $response = $this->write('STLS', 1);
104 if ($response[0][0] == '+') {
105 $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
106
107 if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
108 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
109 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
110 }
111 do {
112 if (isset($res)) {
113 sleep(0.1);
114 }
115 $res = stream_socket_enable_crypto($this->socket, TRUE, $crypto_method);
116 } while ($res === 0);
117 if ($res === FALSE) {
118 throw new fConnectivityException('Error establishing secure connection');
119 }
120 }
121 } else {
122 if (!$openSsl) {
123 DebugEcho("Socket: OpenSSL not enabled");
124 }
125 if ($this->secure && $use_implicit_tls) {
126 DebugEcho("Socket: Connected via Implicit TLS");
127 }
128 }
129
130 $authenticated = FALSE;
131 // commented out since some mail servers disconnect if APOP is not supported
132 // if (isset($match[0])) {
133 // $response = $this->write('APOP ' . $this->username . ' ' . md5($match[0] . $this->password), 1);
134 // if (isset($response[0]) && $response[0][0] == '+') {
135 // $authenticated = TRUE;
136 // }
137 // }
138
139 if (!$authenticated) {
140 $response = $this->write('USER ' . $this->username, 1);
141 if ($response[0][0] == '+') {
142 $response = $this->write('PASS ' . $this->password, 1);
143 if (isset($response[0][0]) && $response[0][0] == '+') {
144 $authenticated = TRUE;
145 }
146 }
147 }
148
149 if (!$authenticated) {
150 throw new fValidationException('POP3 - Could not connect to %1$s server %2$s on port %3$s. %4$s', strtoupper($this->type), $this->host, $this->port, print_r($response, true));
151 }
152 }
153 }
154
155 public function write($command, $expected = null) {
156
157 if ($this->type == 'imap') {
158 $identifier = 'A' . str_pad($this->command_num++, 4, '0', STR_PAD_LEFT);
159 $command = $identifier . ' ' . $command;
160 }
161
162 if (substr($command, -2) != "\r\n") {
163 $command .= "\r\n";
164 }
165
166 if ((strpos($command, ' LOGIN') === false) & (strpos($command, 'APOP ') === false ) & (strpos($command, 'PASS ') === false )) {
167 DebugEcho("socket write: " . trim($command));
168 } else {
169 DebugEcho("socket write: (login) LOGIN/APOP/PASS");
170 }
171
172 $res = fwrite($this->socket, $command);
173 if ($res == 1) {
174 sleep(3);
175 $res = fwrite($this->socket, $command);
176 }
177
178 if ($res === FALSE || $res === 0) {
179 DebugEcho("Socket: error");
180 DebugDump(error_get_last());
181 throw new fConnectivityException('Unable to write data to %1$s server %2$s on port %3$s', strtoupper($this->type), $this->host, $this->port);
182 }
183
184 if ($this->type == 'imap') {
185 return $this->read('#^' . $identifier . '#');
186 } elseif ($this->type == 'pop3') {
187 return $this->read($expected);
188 }
189 }
190
191 public function read($expect = NULL) {
192
193 $read = array($this->socket);
194 $write = NULL;
195 $except = NULL;
196 $response = array();
197
198 // PHP 5.2.0 to 5.2.5 has a bug on amd64 linux where stream_select()
199 // fails, so we have to fake it - http://bugs.php.net/bug.php?id=42682
200 static $broken_select = NULL;
201 if ($broken_select === NULL) {
202 //$broken_select = strpos(php_uname('m'), '64') !== FALSE && fCore::checkVersion('5.2.0') && !fCore::checkVersion('5.2.6');
203 }
204
205 // Fixes an issue with stream_select throwing a warning on PHP 5.3 on Windows
206 if (fCore::checkOS('windows') && fCore::checkVersion('5.3.0')) {
207 $select = @stream_select($read, $write, $except, $this->timeout);
208 } elseif ($broken_select) {
209 $broken_select_buffer = NULL;
210 $start_time = microtime(TRUE);
211 $i = 0;
212 do {
213 if ($i) {
214 usleep(50000);
215 }
216 $char = fgetc($this->socket);
217 if ($char != "\x00" && $char !== FALSE) {
218 $broken_select_buffer = $char;
219 }
220 $i++;
221 } while ($broken_select_buffer === NULL && microtime(TRUE) - $start_time < $this->timeout);
222 $select = $broken_select_buffer !== NULL;
223 } else {
224 $select = stream_select($read, $write, $except, $this->timeout);
225 }
226
227 if ($select) {
228 while (!feof($this->socket)) {
229 $line = fgets($this->socket);
230 if ($line === FALSE) {
231 break;
232 }
233 $line = substr($line, 0, -2);
234
235 // When we fake select, we have to handle what we've retrieved
236 if ($broken_select && $broken_select_buffer !== NULL) {
237 $line = $broken_select_buffer . $line;
238 $broken_select_buffer = NULL;
239 }
240
241 $response[] = $line;
242
243 // Automatically stop at the termination octet or a bad response
244 if ($this->type == 'pop3' && ($line == '.' || (count($response) == 1 && $response[0][0] == '-'))) {
245 break;
246 }
247
248 if ($expect !== NULL) {
249 $matched_number = is_int($expect) && sizeof($response) == $expect;
250 $matched_regex = is_string($expect) && preg_match($expect, $line);
251 if ($matched_number || $matched_regex) {
252 break;
253 }
254 }
255 }
256 }
257 //fCore::debug("Received:\n" . join("\r\n", $response), true);
258 DebugEcho("socket read (" . count($response) . ') :' . $response[0]);
259 //DebugDump($response);
260
261 if ($this->type == 'pop3') {
262 // Remove the termination octet
263 if ($response && $response[sizeof($response) - 1] == '.') {
264 $response = array_slice($response, 0, -1);
265 }
266 // Remove byte-stuffing
267 $lines = count($response);
268 for ($i = 0; $i < $lines; $i++) {
269 if (strlen($response[$i]) && $response[$i][0] == '.') {
270 $response[$i] = substr($response[$i], 1);
271 }
272 }
273 }
274
275 return $response;
276 }
277
278 public function isPersistant() {
279 return true;
280 }
281 }
282