Esempio n. 1
0
 /**
  * Initiates the connection to the server
  * 
  * @return void
  */
 private function connect()
 {
     if ($this->connection) {
         return;
     }
     $fqdn = fEmail::getFQDN();
     fCore::startErrorCapture(E_WARNING);
     $host = $this->secure ? 'tls://' . $this->host : $this->host;
     $this->connection = fsockopen($host, $this->port, $error_int, $error_string, $this->timeout);
     foreach (fCore::stopErrorCapture('#ssl#i') as $error) {
         throw new fConnectivityException('There was an error connecting to the server. A secure connection was requested, but was not available. Try a non-secure connection instead.');
     }
     if (!$this->connection) {
         throw new fConnectivityException('There was an error connecting to the server');
     }
     stream_set_timeout($this->connection, $this->timeout);
     $response = $this->read('#^220 #');
     if (!$this->find($response, '#^220[ -]#')) {
         throw new fConnectivityException('Unknown SMTP welcome message, %1$s, from server %2$s on port %3$s', join("\r\n", $response), $this->host, $this->port);
     }
     // Try sending the ESMTP EHLO command, but fall back to normal SMTP HELO
     $response = $this->write('EHLO ' . $fqdn, '#^250 #m');
     if ($this->find($response, '#^500#')) {
         $response = $this->write('HELO ' . $fqdn, 1);
     }
     // If STARTTLS is available, use it
     if (!$this->secure && extension_loaded('openssl') && $this->find($response, '#^250[ -]STARTTLS#')) {
         $response = $this->write('STARTTLS', '#^220 #');
         $affirmative = $this->find($response, '#^220[ -]#');
         if ($affirmative) {
             do {
                 if (isset($res)) {
                     sleep(0.1);
                 }
                 $res = stream_socket_enable_crypto($this->connection, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT);
             } while ($res === 0);
         }
         if (!$affirmative || $res === FALSE) {
             throw new fConnectivityException('Error establishing secure connection');
         }
         $response = $this->write('EHLO ' . $fqdn, '#^250 #m');
     }
     $this->max_size = 0;
     if ($match = $this->find($response, '#^250[ -]SIZE\\s+(\\d+)$#')) {
         $this->max_size = $match[0][1];
     }
     $this->pipelining = (bool) $this->find($response, '#^250[ -]PIPELINING$#');
     $auth_methods = array();
     if ($match = $this->find($response, '#^250[ -]AUTH[ =](.*)$#')) {
         $auth_methods = array_map('strtoupper', explode(' ', $match[0][1]));
     }
     if (!$auth_methods || !$this->username) {
         return;
     }
     if (in_array('DIGEST-MD5', $auth_methods)) {
         $response = $this->write('AUTH DIGEST-MD5', 1);
         $this->handleErrors($response);
         $match = $this->find($response, '#^334 (.*)$#');
         $challenge = base64_decode($match[0][1]);
         preg_match_all('#(?<=,|^)(\\w+)=("[^"]+"|[^,]+)(?=,|$)#', $challenge, $matches, PREG_SET_ORDER);
         $request_params = array();
         foreach ($matches as $_match) {
             $request_params[$_match[1]] = $_match[2][0] == '"' ? substr($_match[2], 1, -1) : $_match[2];
         }
         $missing_qop_auth = !isset($request_params['qop']) || !in_array('auth', explode(',', $request_params['qop']));
         $missing_nonce = empty($request_params['nonce']);
         if ($missing_qop_auth || $missing_nonce) {
             throw new fUnexpectedException('The SMTP server %1$s on port %2$s claims to support DIGEST-MD5, but does not seem to provide auth functionality', $this->host, $this->port);
         }
         if (!isset($request_params['realm'])) {
             $request_params['realm'] = '';
         }
         // Algorithm from http://www.ietf.org/rfc/rfc2831.txt
         $realm = $request_params['realm'];
         $nonce = $request_params['nonce'];
         $cnonce = fCryptography::randomString('32', 'hexadecimal');
         $nc = '00000001';
         $digest_uri = 'smtp/' . $this->host;
         $a1 = md5($this->username . ':' . $realm . ':' . $this->password, TRUE) . ':' . $nonce . ':' . $cnonce;
         $a2 = 'AUTHENTICATE:' . $digest_uri;
         $response = md5(md5($a1) . ':' . $nonce . ':' . $nc . ':' . $cnonce . ':auth:' . md5($a2));
         $response_params = array('charset=utf-8', 'username="******"', 'realm="' . $realm . '"', 'nonce="' . $nonce . '"', 'nc=' . $nc, 'cnonce="' . $cnonce . '"', 'digest-uri="' . $digest_uri . '"', 'response=' . $response, 'qop=auth');
         $response = $this->write(base64_encode(join(',', $response_params)), 2);
     } elseif (in_array('CRAM-MD5', $auth_methods)) {
         $response = $this->write('AUTH CRAM-MD5', 1);
         $match = $this->find($response, '#^334 (.*)$#');
         $challenge = base64_decode($match[0][1]);
         $response = $this->write(base64_encode($this->username . ' ' . fCryptography::hashHMAC('md5', $challenge, $this->password)), 1);
     } elseif (in_array('LOGIN', $auth_methods)) {
         $response = $this->write('AUTH LOGIN', 1);
         $this->write(base64_encode($this->username), 1);
         $response = $this->write(base64_encode($this->password), 1);
     } elseif (in_array('PLAIN', $auth_methods)) {
         $response = $this->write('AUTH PLAIN ' . base64_encode($this->username . "" . $this->username . "" . $this->password), 1);
     }
     if ($this->find($response, '#^535[ -]#')) {
         throw new fValidationException('The username and password provided were not accepted for the SMTP server %1$s on port %2$s', $this->host, $this->port);
     }
     if (!array_filter($response)) {
         throw new fConnectivityException('No response was received for the authorization request');
     }
 }