public function connect()
 {
     $host = $this->account->host;
     $port = $this->account->port;
     $user = $this->account->user;
     $proxy_user = $this->account->proxy_user;
     $pass = $this->account->pass;
     $zone = $this->account->zone;
     $auth_type = $this->account->auth_type;
     // if we're going to use PAM, set up the socket context
     // options for SSL connections when we open the connection
     if (strcasecmp($auth_type, "PAM") == 0) {
         $ssl_opts = array('ssl' => array());
         if (array_key_exists('ssl', $GLOBALS['PRODS_CONFIG'])) {
             $ssl_conf = $GLOBALS['PRODS_CONFIG']['ssl'];
             if (array_key_exists('verify_peer', $ssl_conf)) {
                 if (strcasecmp("true", $ssl_conf['verify_peer']) == 0) {
                     $ssl_opts['ssl']['verify_peer'] = true;
                 }
             }
             if (array_key_exists('allow_self_signed', $ssl_conf)) {
                 if (strcasecmp("true", $ssl_conf['allow_self_signed']) == 0) {
                     $ssl_opts['ssl']['allow_self_signed'] = true;
                 }
             }
             if (array_key_exists('cafile', $ssl_conf)) {
                 $ssl_opts['ssl']['cafile'] = $ssl_conf['cafile'];
             }
             if (array_key_exists('capath', $ssl_conf)) {
                 $ssl_opts['ssl']['capath'] = $ssl_conf['capath'];
             }
         }
         $ssl_ctx = stream_context_get_default($ssl_opts);
         $sock_timeout = ini_get("default_socket_timeout");
         $conn = @stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, $sock_timeout, STREAM_CLIENT_CONNECT, $ssl_ctx);
     } else {
         $conn = @fsockopen($host, $port, $errno, $errstr);
     }
     if (!$conn) {
         throw new RODSException("Connection to '{$host}:{$port}' failed.1: ({$errno}){$errstr}. ", "SYS_SOCK_OPEN_ERR");
     }
     $this->conn = $conn;
     // connect to RODS server
     $msg = RODSMessage::packConnectMsg($user, $proxy_user, $zone);
     fwrite($conn, $msg);
     $msg = new RODSMessage();
     $intInfo = $msg->unpack($conn);
     if ($intInfo < 0) {
         throw new RODSException("Connection to '{$host}:{$port}' failed.2. User: {$proxy_user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
     }
     // are we doing PAM authentication
     if (strcasecmp($auth_type, "PAM") == 0) {
         // Ask server to turn on SSL
         $req_packet = new RP_sslStartInp();
         $msg = new RODSMessage("RODS_API_REQ_T", $req_packet, $GLOBALS['PRODS_API_NUMS']['SSL_START_AN']);
         fwrite($conn, $msg->pack());
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             throw new RODSException("Connection to '{$host}:{$port}' failed.ssl1. User: {$proxy_user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
         // Turn on SSL on our side
         // TSM Feb 2016: changed crypto method from TLS_CLIENT to SSLv23_CLIENT  because iRODS4.1 expects at least TLS1.2
         //               in PHP 5.4 the TLS_CLIENT will NOT negotiate TLS 1.2 where SSLv23 does so.
         //               see https://bugs.php.net/bug.php?id=65329
         if (!stream_socket_enable_crypto($conn, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT)) {
             throw new RODSException("Error turning on SSL on connection to server '{$host}:{$port}'.");
         }
         // all good ... do the PAM authentication over the encrypted connection
         // FIXME: '24', the TTL in hours, should be a configuration option.
         $req_packet = new RP_pamAuthRequestInp($proxy_user, $pass, 24);
         $msg = new RODSMessage("RODS_API_REQ_T", $req_packet, $GLOBALS['PRODS_API_NUMS']['PAM_AUTH_REQUEST_AN']);
         fwrite($conn, $msg->pack());
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             throw new RODSException("PAM auth failed at server '{$host}:{$port}' User: {$proxy_user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
         // Update the account object with the temporary password
         // and set the auth_type to irods for this connection
         $pack = $msg->getBody();
         $pass = $this->account->pass = $pack->irodsPamPassword;
         // Done authentication ... turn ask the server to turn off SSL
         $req_packet = new RP_sslEndInp();
         $msg = new RODSMessage("RODS_API_REQ_T", $req_packet, $GLOBALS['PRODS_API_NUMS']['SSL_END_AN']);
         fwrite($conn, $msg->pack());
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             throw new RODSException("Connection to '{$host}:{$port}' failed.ssl2. User: {$proxy_user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
         // De-activate SSL on the connection
         stream_socket_enable_crypto($conn, false);
         // CJS: For whatever reason some trash is left over for us
         // to read after the SSL shutdown.
         // We need to read and discard those bytes so they don't
         // get in the way of future API responses.
         //
         // There used to be a while(select() > 0){fread(1)} loop
         // here, but that proved to be unreliable, most likely
         // because sometimes not all trash bytes have yet been
         // received at that point. This caused PAM logins to fail
         // randomly.
         //
         // The following fread() call reads all remaining bytes in
         // the current packet (or so it seems).
         //
         // Testing shows there's always exactly 31 bytes to read.
         fread($conn, 1024);
     }
     // request authentication
     $msg = new RODSMessage("RODS_API_REQ_T", NULL, $GLOBALS['PRODS_API_NUMS']['AUTH_REQUEST_AN']);
     fwrite($conn, $msg->pack());
     // get chalange string
     $msg = new RODSMessage();
     $intInfo = $msg->unpack($conn);
     if ($intInfo < 0) {
         throw new RODSException("Connection to '{$host}:{$port}' failed.3. User: {$proxy_user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
     }
     $pack = $msg->getBody();
     $challenge_b64encoded = $pack->challenge;
     $challenge = base64_decode($challenge_b64encoded);
     // encode chalange with passwd
     $pad_pass = str_pad($pass, MAX_PASSWORD_LEN, "");
     $pwmd5 = md5($challenge . $pad_pass, true);
     for ($i = 0; $i < strlen($pwmd5); $i++) {
         //"escape" the string in RODS way...
         if (ord($pwmd5[$i]) == 0) {
             $pwmd5[$i] = chr(1);
         }
     }
     $response = base64_encode($pwmd5);
     // set response
     $resp_packet = new RP_authResponseInp($response, $proxy_user);
     $msg = new RODSMessage("RODS_API_REQ_T", $resp_packet, $GLOBALS['PRODS_API_NUMS']['AUTH_RESPONSE_AN']);
     fwrite($conn, $msg->pack());
     // check if we are connected
     // get chalange string
     $msg = new RODSMessage();
     $intInfo = $msg->unpack($conn);
     if ($intInfo < 0) {
         $this->disconnect();
         $scrambledPass = preg_replace("|.|", "*", $pass);
         throw new RODSException("Connection to '{$host}:{$port}' failed.4 (login failed, possible wrong user/passwd). User: {$proxy_user} Pass: {$scrambledPass} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
     }
     $this->connected = true;
     // use ticket if specified
     if (!empty($this->account->ticket)) {
         $ticket_packet = new RP_ticketAdminInp('session', $this->account->ticket);
         $msg = new RODSMessage('RODS_API_REQ_T', $ticket_packet, 723);
         fwrite($conn, $msg->pack());
         // get response
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             $this->disconnect();
             throw new RODSException('Cannot set session ticket.', $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
     }
 }
 public function connect()
 {
     $host = $this->account->host;
     $port = $this->account->port;
     $user = $this->account->user;
     $pass = $this->account->pass;
     $zone = $this->account->zone;
     $auth_type = $this->account->auth_type;
     // if we're going to use PAM, set up the socket context
     // options for SSL connections when we open the connection
     if (strcasecmp($auth_type, "PAM") == 0) {
         $ssl_opts = array('ssl' => array());
         if (array_key_exists('ssl', $GLOBALS['PRODS_CONFIG'])) {
             $ssl_conf = $GLOBALS['PRODS_CONFIG']['ssl'];
             if (array_key_exists('verify_peer', $ssl_conf)) {
                 if (strcasecmp("true", $ssl_conf['verify_peer']) == 0) {
                     $ssl_opts['ssl']['verify_peer'] = true;
                 }
             }
             if (array_key_exists('allow_self_signed', $ssl_conf)) {
                 if (strcasecmp("true", $ssl_conf['allow_self_signed']) == 0) {
                     $ssl_opts['ssl']['allow_self_signed'] = true;
                 }
             }
             if (array_key_exists('cafile', $ssl_conf)) {
                 $ssl_opts['ssl']['cafile'] = $ssl_conf['cafile'];
             }
             if (array_key_exists('capath', $ssl_conf)) {
                 $ssl_opts['ssl']['capath'] = $ssl_conf['capath'];
             }
         }
         $ssl_ctx = stream_context_get_default($ssl_opts);
         $sock_timeout = ini_get("default_socket_timeout");
         $conn = @stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, $sock_timeout, STREAM_CLIENT_CONNECT, $ssl_ctx);
     } else {
         $conn = @fsockopen($host, $port, $errno, $errstr);
     }
     if (!$conn) {
         throw new RODSException("Connection to '{$host}:{$port}' failed.1: ({$errno}){$errstr}. ", "SYS_SOCK_OPEN_ERR");
     }
     $this->conn = $conn;
     // connect to RODS server
     $msg = RODSMessage::packConnectMsg($user, $zone);
     fwrite($conn, $msg);
     $msg = new RODSMessage();
     $intInfo = $msg->unpack($conn);
     if ($intInfo < 0) {
         throw new RODSException("Connection to '{$host}:{$port}' failed.2. User: {$user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
     }
     // are we doing PAM authentication
     if (strcasecmp($auth_type, "PAM") == 0) {
         // Ask server to turn on SSL
         $req_packet = new RP_sslStartInp();
         $msg = new RODSMessage("RODS_API_REQ_T", $req_packet, $GLOBALS['PRODS_API_NUMS']['SSL_START_AN']);
         fwrite($conn, $msg->pack());
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             throw new RODSException("Connection to '{$host}:{$port}' failed.ssl1. User: {$user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
         // Turn on SSL on our side
         if (!stream_socket_enable_crypto($conn, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
             throw new RODSException("Error turning on SSL on connection to server '{$host}:{$port}'.");
         }
         // all good ... do the PAM authentication over the encrypted connection
         $req_packet = new RP_pamAuthRequestInp($user, $pass, -1);
         $msg = new RODSMessage("RODS_API_REQ_T", $req_packet, $GLOBALS['PRODS_API_NUMS']['PAM_AUTH_REQUEST_AN']);
         fwrite($conn, $msg->pack());
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             throw new RODSException("PAM auth failed at server '{$host}:{$port}' User: {$user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
         // Update the account object with the temporary password
         // and set the auth_type to irods for this connection
         $pack = $msg->getBody();
         $pass = $this->account->pass = $pack->irodsPamPassword;
         // Done authentication ... turn ask the server to turn off SSL
         $req_packet = new RP_sslEndInp();
         $msg = new RODSMessage("RODS_API_REQ_T", $req_packet, $GLOBALS['PRODS_API_NUMS']['SSL_END_AN']);
         fwrite($conn, $msg->pack());
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             throw new RODSException("Connection to '{$host}:{$port}' failed.ssl2. User: {$user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
         // De-activate SSL on the connection
         stream_socket_enable_crypto($conn, false);
         // nasty hack ... some characters are left over to be read
         // from the socket after the SSL shutdown, and I can't
         // figure out how to consume them via SSL routines, so I
         // just read them and throw them away. They need to be consumed
         // or later reads get out of sync with the API responses
         $r = array($conn);
         $w = $e = null;
         while (stream_select($r, $w, $e, 0) > 0) {
             $s = fread($conn, 1);
         }
     }
     // request authentication
     $msg = new RODSMessage("RODS_API_REQ_T", NULL, $GLOBALS['PRODS_API_NUMS']['AUTH_REQUEST_AN']);
     fwrite($conn, $msg->pack());
     // get chalange string
     $msg = new RODSMessage();
     $intInfo = $msg->unpack($conn);
     if ($intInfo < 0) {
         throw new RODSException("Connection to '{$host}:{$port}' failed.3. User: {$user} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
     }
     $pack = $msg->getBody();
     $challenge_b64encoded = $pack->challenge;
     $challenge = base64_decode($challenge_b64encoded);
     // encode chalange with passwd
     $pad_pass = str_pad($pass, MAX_PASSWORD_LEN, "");
     $pwmd5 = md5($challenge . $pad_pass, true);
     for ($i = 0; $i < strlen($pwmd5); $i++) {
         //"escape" the string in RODS way...
         if (ord($pwmd5[$i]) == 0) {
             $pwmd5[$i] = chr(1);
         }
     }
     $response = base64_encode($pwmd5);
     // set response
     $resp_packet = new RP_authResponseInp($response, $user);
     $msg = new RODSMessage("RODS_API_REQ_T", $resp_packet, $GLOBALS['PRODS_API_NUMS']['AUTH_RESPONSE_AN']);
     fwrite($conn, $msg->pack());
     // check if we are connected
     // get chalange string
     $msg = new RODSMessage();
     $intInfo = $msg->unpack($conn);
     if ($intInfo < 0) {
         $this->disconnect();
         throw new RODSException("Connection to '{$host}:{$port}' failed.4 (login failed, possible wrong user/passwd). User: {$user} Pass: {$pass} Zone: {$zone}", $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
     }
     $this->connected = true;
     // use ticket if specified
     if (!empty($this->account->ticket)) {
         $ticket_packet = new RP_ticketAdminInp('session', $this->account->ticket);
         $msg = new RODSMessage('RODS_API_REQ_T', $ticket_packet, 723);
         fwrite($conn, $msg->pack());
         // get response
         $msg = new RODSMessage();
         $intInfo = $msg->unpack($conn);
         if ($intInfo < 0) {
             $this->disconnect();
             throw new RODSException('Cannot set session ticket.', $GLOBALS['PRODS_ERR_CODES_REV']["{$intInfo}"]);
         }
     }
 }