/**
  * Creates a DigestChallenge object from a string
  *
  * Example input string:
  * <pre>
  *   realm="example.com",nonce="GMybUaOM4lpMlJbeRwxOLzTalYDwLAxv/sLf8de4DPA=",
  *   qop="auth,auth-int,auth-conf",cipher="rc4-40,rc4-56,rc4",charset=utf-8,
  *   algorithm=md5-sess    
  * </pre>
  *
  * @param   string s
  * @return  security.sasl.DigestChallenge
  * @throws  lang.FormatException
  */
 public static function fromString($s)
 {
     with($challenge = new DigestChallenge());
     $s .= ',';
     while ($p = strpos($s, '=')) {
         $key = substr($s, 0, $p);
         $t = '"' == $s[$p + 1] ? strpos($s, '"', $p + 2) + 1 : strpos($s, ',', $p + 1);
         $value = trim(substr($s, $p + 1, $t - $p - 1), '"');
         switch ($key) {
             case 'realm':
                 $challenge->setRealm($value);
                 break;
             case 'domain':
                 $challenge->setDomain($value);
                 break;
             case 'nonce':
                 $challenge->setNonce($value);
                 break;
             case 'qop':
                 $challenge->setQop(explode(',', strtolower($value)));
                 break;
             case 'cipher':
                 $challenge->setCipher(explode(',', strtolower($value)));
                 break;
             case 'charset':
                 $challenge->setCharset($value);
                 break;
             case 'algorithm':
                 $challenge->setAlgorithm($value);
                 break;
             case 'stale':
                 $challenge->setStale(0 == strcasecmp($value, 'true'));
                 break;
             case 'opaque':
                 $challenge->setOpaque($value);
                 break;
             case 'maxbuf':
                 $challenge->setMaxbuf(intval($value));
                 break;
             default:
                 throw new FormatException('Unrecognized key "' . $key . '"');
         }
         $s = substr($s, $t + 1);
     }
     return $challenge;
 }