/**
  * Retrieve response string
  *
  * @return  string
  */
 public function getString()
 {
     $cnonce = base64_encode(bin2hex(HMAC_MD5::hash(microtime())));
     $ncount = sprintf('%08d', $this->ncount);
     $username_value = HMAC_MD5::hash(sprintf('%s:%s:%s', $this->_encode($this->user), $this->_encode($this->realm), $this->_encode($this->pass)));
     // If authzid is specified, then A1 is
     //
     //    A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
     //         ":", nonce-value, ":", cnonce-value, ":", authzid-value }
     //
     // If authzid is not specified, then A1 is
     //
     //    A1 = { H( { username-value, ":", realm-value, ":", passwd } ),
     //         ":", nonce-value, ":", cnonce-value }
     //
     $a1 = sprintf('%s:%s:%s%s', $username_value, $this->nonce, $cnonce, $this->authzid ? ':' . $this->_encode($this->authzid) : '');
     // If the "qop" directive's value is "auth", then A2 is:
     //
     //     A2       = { "AUTHENTICATE:", digest-uri-value }
     //
     //  If the "qop" value is "auth-int" or "auth-conf" then A2 is:
     //
     //     A2       = { "AUTHENTICATE:", digest-uri-value,
     //              ":00000000000000000000000000000000" }
     switch ($this->qop) {
         case 'auth':
             $a2 = 'AUTHENTICATE:' . $this->digestUri;
             break;
         case 'auth-int':
         case 'auth-conf':
             $a2 = 'AUTHENTICATE:' . $this->digestUri . ':00000000000000000000000000000000';
             break;
     }
     // response-value  =
     //
     //     HEX( KD ( HEX(H(A1)),
     //             { nonce-value, ":" nc-value, ":",
     //               cnonce-value, ":", qop-value, ":", HEX(H(A2)) }))
     $response_value = bin2hex(HMAC_MD5::hash(sprintf('%s:%s:%s:%s:%s:%s', bin2hex(HMAC_MD5::hash($a1)), $this->nonce, $ncount, $cnonce, $this->qop, bin2hex(HMAC_MD5::hash($a2)))));
     return sprintf('%susername="******",realm="%s",nonce="%s",nc=%s,' . 'cnonce="%s",digest-uri="%s",response=%s,qop=%s%s', $this->charset ? 'charset=' . $this->charset . ',' : '', $this->_encode($this->user), $this->_encode($this->realm), $this->nonce, $ncount, $cnonce, $this->digestUri, $response_value, $this->qop, $this->authzid ? ',authzid="' . $this->_encode($this->authzid) . '"' : '');
 }
 /**
  * Authenticate
  *
  * Supported methods:
  * <ul>
  *   <li>PLAIN</li>
  *   <li>LOGIN</li>
  *   <li>DIGEST-MD5</li>
  *   <li>CRAM-MD5</li>
  * </ul>
  *
  * @param   string method one of the SIEVE_SASL_* constants
  * @param   string user
  * @param   string pass
  * @param   string auth default NULL
  * @return  bool success
  * @throws  lang.IllegalArgumentException when the specified method is not supported
  */
 public function authenticate($method, $user, $pass, $auth = NULL)
 {
     if (!$this->hasAuthenticationMethod($method)) {
         throw new IllegalArgumentException('Authentication method ' . $method . ' not supported');
     }
     // Check whether we want to impersonate
     if (NULL === $auth) {
         $auth = $user;
     }
     // Send auth request depending on specified authentication method
     switch ($method) {
         case SIEVE_SASL_PLAIN:
             $e = base64_encode($auth . "" . $user . "" . $pass);
             $this->_sendcmd('AUTHENTICATE "PLAIN" {%d+}', strlen($e));
             $this->_sendcmd($e);
             break;
         case SIEVE_SASL_LOGIN:
             $this->_sendcmd('AUTHENTICATE "LOGIN"');
             $ue = base64_encode($user);
             $this->_sendcmd('{%d+}', strlen($ue));
             $this->_sendcmd($ue);
             $pe = base64_encode($pass);
             $this->_sendcmd('{%d+}', strlen($pe));
             $this->_sendcmd($pe);
             break;
         case SIEVE_SASL_DIGEST_MD5:
             $this->_sendcmd('AUTHENTICATE "DIGEST-MD5"');
             // Read server challenge. Example (decoded):
             //
             // realm="example.com",nonce="GMybUaOM4lpMlJbeRwxOLzTalYDwLAxv/sLf8de4DPA=",
             // qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,rc4",charset=utf-8,
             // algorithm=md5-sess
             //
             // See also xp://security.sasl.DigestChallenge
             $len = $this->_sock->readLine(0x400);
             $str = base64_decode($this->_sock->readLine());
             $this->cat && $this->cat->debug('Challenge (length ' . $len . '):', $str);
             $challenge = DigestChallenge::fromString($str);
             $response = $challenge->responseFor(DC_QOP_AUTH, $user, $pass, $auth);
             $this->cat && $this->cat->debug($challenge, $response);
             // Build and send challenge response
             $response->setDigestUri('sieve/' . $this->_sock->host);
             $cmd = $response->getString();
             $this->cat && $this->cat->debug('Sending challenge response', $cmd);
             $this->_sendcmd('"%s"', base64_encode($cmd));
             // Finally, read the response auth
             $len = $this->_sock->readLine();
             $str = base64_decode($this->_sock->readLine());
             $this->cat && $this->cat->debug('Response auth (length ' . $len . '):', $str);
             return TRUE;
         case SIEVE_SASL_CRAM_MD5:
             $this->_sendcmd('AUTHENTICATE "CRAM-MD5"');
             // Read server challenge. Example (decoded):
             //
             // <*****@*****.**>
             //
             // See also rfc://2195
             $len = $this->_sock->readLine(0x400);
             $challenge = base64_decode($this->_sock->readLine());
             $this->cat && $this->cat->debug('Challenge (length ' . $len . '):', $challenge);
             // Build response and send it
             $response = sprintf('%s %s', $user, bin2hex(HMAC_MD5::hash($challenge, $pass)));
             $this->cat && $this->cat->debug('Sending challenge response', $response);
             $this->_sendcmd('"%s"', base64_encode($response));
             break;
         default:
             throw new IllegalArgumentException('Authentication method ' . $method . ' not implemented');
     }
     // Read the response. Examples:
     //
     // - OK
     // - NO ("SASL" "authentication failure") "Authentication error"
     return $this->_response(TRUE);
 }
 /**
  * Create a new checksum from a file object
  *
  * @param   io.File file
  * @param   string key default NULL
  * @return  security.checksum.HMAC_MD5
  */
 public static function fromFile($file, $key = NULL)
 {
     return new HMAC_MD5(HMAC_MD5::hash(FileUtil::getContents($file), $key));
 }