gt:     abstract void update(StringValue value);
context: /*
   * Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
   *
   * This file is part of Resin(R) Open Source
   *
   * Each copy or derived work must preserve the copyright notice and this
   * notice unmodified.
   *
   * Resin Open Source is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * Resin Open Source is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
   * of NON-INFRINGEMENT.  See the GNU General Public License for more
   * details.
   *
   * You should have received a copy of the GNU General Public License
   * along with Resin Open Source; if not, write to the
   *
   *   Free Software Foundation, Inc.
   *   59 Temple Place, Suite 330
   *   Boston, MA 02111-1307  USA
   *
   * @author Scott Ferguson
   */
  
  package com.caucho.quercus.lib;
  
  import com.caucho.config.ConfigException;
  import com.caucho.quercus.annotation.Optional;
  import com.caucho.quercus.env.*;
  import com.caucho.quercus.module.*;
  import com.caucho.util.*;
  import com.caucho.vfs.*;
  
  import java.io.*;
  import java.security.*;
  import java.util.*;
  import java.util.logging.Level;
  import java.util.logging.Logger;
  
  import javax.crypto.*;
  import javax.crypto.spec.*;
  
  /**
   * Hash functions.
   *
   * This module uses the {@link MessageDigest} class to calculate
   * digests. Typical java installations support MD2, MD5, SHA1, SHA256, SHA384,
   * and SHA512.
   */
  public class HashModule extends AbstractQuercusModule {
    private static final L10N L = new L10N(HashModule.class);
    private static final Logger log
      = Logger.getLogger(HashModule.class.getName());
  
    public static final int HASH_HMAC = 1;
  
    private static HashMap<String,String> _algorithmMap
      = new HashMap<String,String>();
  
    public HashModule()
    {
    }
  
    public String []getLoadedExtensions()
    {
      return new String[] { "hash" };
    }
  
    /**
     * Hashes a string
     */
    public Value hash(Env env,
                      String algorithm,
                      StringValue string,
                      @Optional boolean isBinary)
    {
      try {
        algorithm = getAlgorithm(algorithm);
        
        MessageDigest digest = MessageDigest.getInstance(algorithm);
        
        int len = string.length();
  
        for (int i = 0; i < len; i++) {
          digest.update((byte) string.charAt(i));
        }
  
        byte []bytes = digest.digest();
  
        return hashToValue(env, bytes, isBinary);
      } catch (NoSuchAlgorithmException e) {
        env.error(L.l("'{0}' is an unknown algorithm", algorithm), e);
  
        return BooleanValue.FALSE;
      }
    }
  
    /**
     * Returns the list of known algorithms
     */
    public static Value hash_algos(Env env)
    {
      ArrayValue array = new ArrayValueImpl();
  
      for (String name : _algorithmMap.keySet()) {
        array.put(env.createString(name));
      }
  
      Collection<String> values = _algorithmMap.values();
  
      for (String name : Security.getAlgorithms("MessageDigest")) {
        if (! values.contains(name))
          array.put(env.createString(name));
      }
      
      return array;
    }
  
    /**
     * Copies a hash instance
     */
    public HashContext hash_copy(HashContext context)
    {
      if (context != null)
        return context.copy();
      else
        return null;
    }
  
    /**
     * Hashes a file
     */
    public Value hash_file(Env env,
                           String algorithm,
                           Path path,
                           @Optional boolean isBinary)
    {
      try {
        algorithm = getAlgorithm(algorithm);
        
        MessageDigest digest = MessageDigest.getInstance(algorithm);
  
        TempBuffer tempBuffer = TempBuffer.allocate();
        byte []buffer = tempBuffer.getBuffer();
        ReadStream is = path.openRead();
        
        try {
          int len;
  
          while ((len = is.read(buffer, 0, buffer.length)) > 0) {
            digest.update(buffer, 0, len);
          }
  
          byte []bytes = digest.digest();
  
          return hashToValue(env, bytes, isBinary);
        } finally {
          TempBuffer.free(tempBuffer);
          
          is.close();
        }
      } catch (NoSuchAlgorithmException e) {
        env.error(L.l("'{0}' is an unknown algorithm", algorithm), e);
  
        return BooleanValue.FALSE;
      } catch (IOException e) {
        env.error(L.l("'{0}' is an unknown file", path), e);
  
        return BooleanValue.FALSE;
      }
    }
  
    /**
     * Returns the final hash value
     */
    public Value hash_final(Env env,
                            HashContext context,
                            @Optional boolean isBinary)
    {
      if (context == null)
        return BooleanValue.FALSE;
  
      return hashToValue(env, context.digest(), isBinary);
    }
  
    /**
     * Hashes a string with the algorithm.
     */
    public Value hash_hmac(Env env,
  			 String algorithm,
  			 StringValue data,
  			 StringValue key,
  			 @Optional boolean isBinary)
    {
      algorithm = getAlgorithm(algorithm);
      
      HashContext context = hash_init(env, algorithm, HASH_HMAC, key);
      
      hash_update(env, context, data);
  
      return hash_final(env, context, isBinary);
    }
  
    /**
     * Hashes a file with the algorithm.
     */
    public Value hash_hmac_file(Env env,
  			      String algorithm,
  			      Path path,
  			      StringValue key,
  			      @Optional boolean isBinary)
    {
      algorithm = getAlgorithm(algorithm);
      
      HashContext context = hash_init(env, algorithm, HASH_HMAC, key);
      
      hash_update_file(env, context, path);
  
      return hash_final(env, context, isBinary);
    }
  
    /**
     * Initialize a hash context.
     */
    public HashContext hash_init(Env env,
                                 String algorithm,
                                 @Optional int options,
                                 @Optional StringValue keyString)
    {
      try {
        algorithm = getAlgorithm(algorithm);
        
        if (options == HASH_HMAC) {
          algorithm = "Hmac" + algorithm;
  
          Mac mac = Mac.getInstance(algorithm);
  
          int keySize = 64;
  
          // php/530c
          if (keyString != null)
            keySize = keyString.length();
  
          byte []keyBytes = new byte[keySize];
  
          for (int i = 0; i < keyString.length(); i++) {
            keyBytes[i] = (byte) keyString.charAt(i);
          }
  
          Key key = new SecretKeySpec(keyBytes, "dsa");
          mac.init(key);
  
          return new HashMacContext(mac);
        }
        else {
          MessageDigest md = MessageDigest.getInstance(algorithm);
  
          return new HashDigestContext(md);
        }
      } catch (Exception e) {
        env.error(L.l("hash_init: '{0}' is an unknown algorithm",
  		    algorithm));
  
        return null;
      }
    }
  
    /**
     * Updates the hash with more data
     */
    public Value hash_update(Env env,
                             HashContext context,
                             StringValue value)
    {
      if (context == null)
        return BooleanValue.FALSE;
  
      context.update(value);
  
      return BooleanValue.TRUE;
    }
  
    /**
     * Updates the hash with more data
     */
    public Value hash_update_file(Env env,
                                  HashContext context,
                                  Path path)
    {
      if (context == null)
        return BooleanValue.FALSE;
  
      TempBuffer tempBuffer = TempBuffer.allocate();
      byte []buffer = tempBuffer.getBuffer();
      ReadStream is = null;
  
      try {
        is = path.openRead();
        
        int len;
  
        while ((len = is.read(buffer, 0, buffer.length)) > 0) {
  	context.update(buffer, 0, len);
        }
      } catch (IOException e) {
        log.log(Level.WARNING, e.toString(), e);
      } finally {
        TempBuffer.free(tempBuffer);
  
        if (is != null)
  	is.close();
      }
  
      return BooleanValue.TRUE;
    }
  
    /**
     * Updates the hash with more data
     */
    public int hash_update_stream(Env env,
                                  HashContext context,
                                  InputStream is,
                                  @Optional("-1") int length)
    {
      if (context == null)
        return -1;
  
      if (length < 0)
        length = Integer.MAX_VALUE - 1;
  
      TempBuffer tempBuffer = TempBuffer.allocate();
      byte []buffer = tempBuffer.getBuffer();
      
      int readLength = 0;
  
      try {
        while (length > 0) {
  	int sublen = buffer.length;
  
  	if (length < sublen)
  	  sublen = length;
  
  	int len = is.read(buffer, 0, sublen);
  
  	if (len < 0)
  	  return readLength;
  
  	context.update(buffer, 0, len);
  
  	readLength += len;
  	length -= len;
        }
      } catch (IOException e) {
        log.log(Level.WARNING, e.toString(), e);
      } finally {
        TempBuffer.free(tempBuffer);
      }
  
      return readLength;
    }
    
    // XXX: hash_update_file
    // XXX: hash_update_stream
    // XXX: hash_update
    // XXX: hash
  
    private static Value hashToValue(Env env, byte []bytes, boolean isBinary)
    {
      if (isBinary) {
        StringValue v = env.createBinaryBuilder();
        v.append(bytes, 0, bytes.length);
        return v;
      }
      else {
        StringValue v = env.createUnicodeBuilder();
  
        for (int i = 0; i < bytes.length; i++) {
  	int ch = bytes[i];
  	int d1 = (ch >> 4) & 0xf;
  	int d2 = (ch) & 0xf;
  
  	if (d1 < 10)
  	  v.append((char) ('0' + d1));
  	else
  	  v.append((char) ('a' + d1 - 10));
  
  	if (d2 < 10)
  	  v.append((char) ('0' + d2));
  	else
  	  v.append((char) ('a' + d2 - 10));
        }
  
        return v;
      }
    }
    
    private static String getAlgorithm(String algorithm)
    {
      String name = _algorithmMap.get(algorithm);
      
      if (name != null)
        return name;
      else
        return algorithm;
    }
  
    public abstract static class HashContext
    {
  